[Spring] Authorization code Grant 1 - 카카오 로그인

류재성's avatar
Jun 05, 2024
[Spring] Authorization code Grant 1 - 카카오 로그인

1. 기본 세팅

 
notion image
 
notion image
 
application.yml
server: servlet: encoding: charset: utf-8 force: true port: 8080 spring: mustache: servlet: expose-request-attributes: true expose-session-attributes: true datasource: driver-class-name: org.h2.Driver url: jdbc:h2:mem:test;MODE=MySQL username: sa password: h2: console: enabled: true jpa: hibernate: ddl-auto: create # none, update show-sql: true properties: hibernate: format_sql: true defer-datasource-initialization: true #테이블이 create 되고 난 이후에 sql init 해라 sql: init: data-locations: classpath:db/data.sql

2. 화면 구현하기

join-form.mustache
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>회원가입 페이지</h1> <hr> <form action="/join" method="post"> <input type="text" placeholder="username" value="ssar" name="username"> <br> <input type="password" placeholder="password" value="1234" name="password"> <br> <input type="email" placeholder="email" value="ssar@nate.com" name="email"> <br> <button type="submit">회원가입</button> </form> </body> </html>
 
login-form.mustache
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>로그인 페이지</h1> <hr> <form action="/login" method="post"> <input type="text" placeholder="username" value="ssar" name="username"> <br> <input type="password" placeholder="password" value="1234" name="password"> <br> <button type="submit">로그인</button> </form> <a href="#">카카오 로그인</a> </body> </html>
main.mustache
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>메인(인증된) 페이지 : ssar</h1> <hr> <div> 상품이름 : 바나나 <br> 가격 : 1000 <br> 재고 : 20 <br> <hr> </div> <div> 상품이름 : 바나나 <br> 가격 : 1000 <br> 재고 : 20 <br> <hr> </div> </body> </html>
 

3. 기본 기능 구현하기

User
package org.example.loginapp.user; import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor @Getter @Table(name = "user_tb") @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(unique = true) private String username; private String password; private String email; private String provider ; // facebook , kakao, apple, naver ... oauth 인증을 위한 필드 private String address; @Builder public User(Integer id, String username, String password, String email, String provider,String address) { this.id = id; this.username = username; this.password = password; this.email = email; this.provider = provider; this.address = address; } }
UserController
package org.example.loginapp.user; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @RequiredArgsConstructor @Controller public class UserController { private final UserService userService; private final HttpSession session; @GetMapping("/oauth/callback") public String callback(String code) { System.out.println("콜백 완료 : "+ code); User sessionUser = userService.카카오로그인(); session.setAttribute("sessionUser",sessionUser); return "redirect:/shop"; } @GetMapping("/join-form") public String joinForm() { return "join-form"; } @GetMapping("/login-form") public String loginForm(){ return "login-form"; } @PostMapping("/join") public String join(String username, String password, String email){ userService.회원가입(username,password,email); return "redirect:/login-form"; } @PostMapping("/login") public String login(String username, String password){ User sessionUser = userService.로그인(username,password); session.setAttribute("sessionUser",sessionUser); return "redirect:/shop"; } }
UserRepository
package org.example.loginapp.user; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; public interface UserRepository extends JpaRepository<User, Integer> { //select * from user_tb where username = ? User findByUsername(@Param("username") String username); }
UserService
package org.example.loginapp.user; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @RequiredArgsConstructor @Service public class UserService { private final UserRepository userRepository; @Transactional public void 회원가입(String username, String password,String email){ User user = User.builder() .username(username) .password(password) .email(email) .build(); userRepository.save(user); } public User 로그인(String username, String password){ User user = userRepository.findByUsername(username); if(user == null){ throw new RuntimeException("아이디가 없습니다."); }else { if(user.getPassword().equals(password)){ return user; }else { throw new RuntimeException("비밀번호가 틀렸습니다."); } } } }
 
Shop
package org.example.loginapp.shop; import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor @Getter @Table(name = "shop_tb") @Entity public class Shop { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id ; private String name; // 상품명 private String price; // 가격 private Integer qty ; // 재고 @Builder public Shop(Integer id, String name, String price, Integer qty) { this.id = id; this.name = name; this.price = price; this.qty = qty; } }
ShopController
package org.example.loginapp.shop; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.example.loginapp.user.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @RequiredArgsConstructor @Controller public class ShopController { private final ShopService shopService; private final HttpSession session ; @GetMapping("/shop") public String main(HttpServletRequest request){ User sessionUser = (User) session.getAttribute("sessionUser"); if(sessionUser == null){ throw new RuntimeException("인증된 사용자가 아닙니다"); } List<Shop> shopList = shopService.상품목록(); request.setAttribute("shopList",shopList); return "main"; } }
ShopRepository
package org.example.loginapp.shop; import org.springframework.data.jpa.repository.JpaRepository; public interface ShopRepository extends JpaRepository<Shop, Integer> { }
ShopService
package org.example.loginapp.shop; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; @RequiredArgsConstructor @Service public class ShopService { private final ShopRepository shopRepository; public List<Shop> 상품목록() { List<Shop> shopList = shopRepository.findAll(); return shopList ; } }
 
main.mustache
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>메인(인증된) 페이지 : {{sessionUser.username}}</h1> <hr> {{#shopList}} <div> 상품이름 : {{name}} <br> 가격 : {{price}} <br> 재고 : {{qty}} <br> <hr> </div> {{/shopList}} </body> </html>
 
data.sql
insert into shop_tb(name,price,qty) values ('바나나',1000,10); insert into shop_tb(name,price,qty) values ('딸기',2000,10); insert into shop_tb(name,price,qty) values ('참외',3000,10);
 
 
notion image
 
notion image
 
notion image
 

4. 카카오 OAuth 페이지 둘러보기

 

카카오 개발자 센터

 
notion image
 
카카오 개발자 센터 - 문서 - 카카오 로그인 을 선택한다.
 
notion image
 
로그인 방식을 확인할 수 있다.
 
 
notion image
 
앱 설정에서 설정해야 할 항목을 볼 수 있다.
 
 

5. 필수 설정

 

5.1 플랫폼 등록

 
notion image
 
notion image
 
notion image
 
내 애플리케이션 - 애플리케이션 추가하기를 눌러 새로운 애플리케이션을 추가해준다.
 
notion image
 
notion image
 
notion image
 
추가한 애플리케이션 - 플랫폼을 들어가 어떤 플랫폼인지를 등록한다. 지금은 웹서버기 때문에 web을 선택한다.
 

5.2 카카오 로그인 활성화

 
notion image
 
notion image
 
 
플랫폼 등록 후 하단 등록하러 가기 or 카카오 로그인을 선택 후 활성화 설정을 On 으로 설정한다.
 

5.3 Redirect URI 등록

 
notion image
 
카카오 로그인 하단의 Redirect URI 버튼을 선택한다.
 
notion image
 
💡
Redirect URI는 카카오 로그인 인증 과정에서 사용자가 카카오 로그인 페이지에서 인증을 완료한 후, 인증 결과를 받을 애플리케이션의 URL을 의미한다. 사용자 인증이 성공하거나 실패한 후 카카오가 사용자를 다시 리디렉션할 URL 주소이다.
 
 
UserController
package org.example.loginapp.user; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @RequiredArgsConstructor @Controller public class UserController { private final UserService userService; private final HttpSession session; @GetMapping("/oauth/callback") public String callback(String code) { System.out.println("콜백 완료 : " + code); User sessionUser = userService.카카오로그인(code); session.setAttribute("sessionUser", sessionUser); return "redirect:/shop"; } @GetMapping("/join-form") public String joinForm() { return "join-form"; } @GetMapping("/login-form") public String loginForm() { return "login-form"; } @PostMapping("/join") public String join(String username, String password, String email) { userService.회원가입(username, password, email); return "redirect:/login-form"; } @PostMapping("/login") public String login(String username, String password) { User sessionUser = userService.로그인(username, password); session.setAttribute("sessionUser", sessionUser); return "redirect:/shop"; } }
 
notion image
 
UserController 에 Redirect URI 를 만들어준다.
 
notion image
 

5.4 동의 항목 설정

 
💡
동의 항목은 사용자가 애플리케이션에 로그인할 때 제공할 개인 정보 및 권한에 대해 동의하는 항목들이다. 이는 사용자가 어떤 정보에 접근을 허용할지, 애플리케이션이 어떤 권한을 가질지를 정의한다. 카카오 로그인 동의 항목에는 필수 항목과 선택 항목이 포함되며, 사용자가 로그인할 때 이를 확인하고 동의해야 한다.
 
notion image
 
notion image
 
💡
개인정보 동의항목 심사 신청은 사업자 정보가 있어야 등록할 수 있다. 지금은 테스트용이기 때문에 닉네임만 설정하도록한다. 실제 서비스에선 동의 항목 중 이메일이 가장 중요한 데이터이다. 그래서 이메일을 꼭 받아야하지만 현재는 이메일을 받을 수 없기 때문에 닉네임을 이메일 형태로 만들 예정이다.
 
Share article

{CODE-RYU};