카테고리 없음
카카오 로그인 (OAuth) #2. 카카오 사용자 정보로 회원가입 구현
luminous_dev
2025. 2. 7. 13:54
관심 상품 등록을 했을 때 회원 구분이 필요하기 때문에, 카카오서버에서 받은 사용자 정보를 이용해 회원 가입
현재 회원 (User) 테이블
컬럼명 | 컬럼 타입 | 중복 허용 | 설명 |
id | Long | X | 테이블 ID(PK) |
username | String | X | 회원 ID |
password | String | O | 패스워드 |
String | X | 이메일 주소 | |
role | String | O | 역할 1) 사용자 : USER 2) 관리자 : ADMIN |
- 카카오로 부터 받은 사용자 정보
- kakaoId
- nickname
{
"id": 1632335751,
"properties": {
"nickname": "르탄이",
"profile_image": "http://k.kakaocdn.net/...jpg",
"thumbnail_image": "http://k.kakaocdn.net/...jpg"
},
"kakao_account": {
"profile_needs_agreement": false,
"profile": {
"nickname": "르탄이",
"thumbnail_image_url": "http://k.kakaocdn.net/...jpg",
"profile_image_url": "http://k.kakaocdn.net/...jpg"
},
"has_email": true,
"email_needs_agreement": false,
"is_email_valid": true,
"is_email_verified": true,
"email": "letan@sparta.com"
}
}
테이블 설계 옵션
- 카카오 User 를 위한 테이블 (ex. KakaoUser) 을 하나 더 만든다.
- 장점: 결합도가 낮아짐
- 성격이 다른 유저 별로 분리 → 차후 각 테이블의 변화에 서로 영향을 주지 않음
- 예) 카카오 사용자들만 profile_image 컬럼 추가해서 사용 가능
- 단점: 구현 난이도가 올라감
- 예) 관심상품 등록 시, 회원별로 다른 테이블을 참조해야 함
- 일반 회원: User - Product
- 카카오 회원: KakaoUser - Product
- 예) 관심상품 등록 시, 회원별로 다른 테이블을 참조해야 함
- 장점: 결합도가 낮아짐
-
- 기존 회원 (User) 테이블에 카카오 User 추가
- 장점: 구현이 단순해짐
- 단점: 결합도가 높아짐
- 폼 로그인을 통해 카카오 로그인 사용자의 username, password 를 입력해서 로그인한다면??
문제가 생길 수 있음
- 폼 로그인을 통해 카카오 로그인 사용자의 username, password 를 입력해서 로그인한다면??
- 기존 회원 (User) 테이블에 카카오 User 추가
BUT 회원 (User)테이블에 적용하기로 하였음
변경 후
컬럼명 | 컬럼 타입 | 중복 허용 | 설명 | 카카오 사용자 |
id | Long | X | 테이블 ID(PK) | 테이블 ID(PK) |
username | String | X | 회원 ID | nickname |
password | String | O | 패스워드 | UUID(랜덤 생성) |
String | X | 이메일 주소 | ||
role | String | O | 역할 1) 사용자 : USER 2) 관리자 : ADMIN |
"USER"로 고정 |
kakaoId | String (Nullable) | X | 카카오 로그인 ID | kakaoId |
패스워드를 UUID로 설정한 이유
: 폼 로그인을 통해서 로그인이 되지 않도록
- 카카오 사용자 정보로 회원가입
- User 테이블에 'kakaoId' 추가
kakaoService에 필요시 회원가입하는 코드 추가
회원 가입 처리 코드
private User registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId();
User kakaoUser = userRepository.findByKakaoId(kakaoId).orElse(null);
if (kakaoUser == null) {
// 카카오 사용자 email 동일한 email 가진 회원이 있는지 확인
String kakaoEmail = kakaoUserInfo.getEmail();
User sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if (sameEmailUser != null) {
kakaoUser = sameEmailUser;
// 기존 회원정보에 카카오 Id 추가
kakaoUser = kakaoUser.kakaoIdUpdate(kakaoId);
} else {
// 신규 회원가입
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email: kakao email
String email = kakaoUserInfo.getEmail();
kakaoUser = new User(kakaoUserInfo.getNickname(), encodedPassword, email, UserRoleEnum.USER, kakaoId);
}
userRepository.save(kakaoUser);
}
return kakaoUser;
}
kakaoService
package com.sparta.myselectshop.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.myselectshop.dto.KakaoUserInfoDto;
import com.sparta.myselectshop.entity.User;
import com.sparta.myselectshop.entity.UserRoleEnum;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.UUID;
@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate; //빨간 줄 : RestTemplate을 그냥 Builder로 만들 수 있다
//빨간줄 뜨는 이유 : restTemplate이 bean이 아닌 Rest Template Builder을 통해서 생성할 수 있도록 유도
//Bean을 수동으로 등록해서 관리하는 방법
private final JwtUtil jwtUtil;
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
//3. 필요 시에 회원 가입
User kakaoUser = registerKakaoUserIfNeeded(kakaoUserInfo); //카카오 사용자 정보
//4. JWT 토큰 반환
String createToken = jwtUtil.createToken(kakaoUser.getUsername(), kakaoUser.getRole());
return createToken;
}
//kakaoLogin에서 받은 인증 코드로 토큰 요청
private String getToken(String code) throws JsonProcessingException {
log.info("인가코드 : "+code);
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com") //여긴 정해진 주소를 넣기
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
//kakao Developers에서 요청한대로 http 형태를 만들어서 Rest template으로 API를 요청함
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "13f0c1acd35319458b5893b3dbba382d"); //여기에 RESTAPI 코드 다시 넣어주기
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code); //받은 인가코드 넣기
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //body가 있으니까 POST
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange( //http 요청이 카카오 서버 쪽으로 감
requestEntity,
String.class
); //ResponseEntity<String>에서 반환되는 String이 토큰의 형태로 되어 있음 > 그 토큰을 한번 파싱할 것임 (.readTree(response.getBody()))
// HTTP 응답 (JSON) -> 액세스 토큰 파싱 = 우리가 딱 원하는 액세스 토큰만 딱 뽑아옴
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
//뽑으면 .asText()니까 String값 반환
//맨 위의 String accessToken = getToken(code);의 String accessToken
}
//사용자 정보 요청 (getKakaoUserInfo 메서드)
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
log.info("accessToken : "+accessToken);
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //우리가 url 만든 거 보내주기
.headers(headers)
.body(new LinkedMultiValueMap<>()); //body는 따로 보내줄 필요가 없어서 new~
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class //String 타입으로 받아옴
);
//Long 타입으로 해당하는 id 받아오는 방법
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
//해댱하는 id값들을 받아올 수 있음
Long id = jsonNode.get("id").asLong();
//properties 중에서 nickname 값을 가져옴
String nickname = jsonNode.get("properties")
.get("nickname").asText();
//properties 중에서 email 값을 가져옴
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
//KakaoUserInfoDto에 값을 넣어줌
return new KakaoUserInfoDto(id, nickname, email);
}
private User registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId(); //카카오 아이디 가져오기
//user테이블에서 넣어준 kakaoId가 있으면 회원가입이 된거고 없으면 회원 가입을 한 적이 없는 것임
User kakaoUser = userRepository.findByKakaoId(kakaoId).orElse(null);
if (kakaoUser == null) {
// 카카오 사용자 email 동일한 email 가진 회원이 있는지 확인
String kakaoEmail = kakaoUserInfo.getEmail();
User sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if (sameEmailUser != null) {
kakaoUser = sameEmailUser;
// 기존 회원정보에 카카오 Id 추가
kakaoUser = kakaoUser.kakaoIdUpdate(kakaoId);
} else {
// 신규 회원가입
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email: kakao email
String email = kakaoUserInfo.getEmail();
kakaoUser = new User(kakaoUserInfo.getNickname(), encodedPassword, email, UserRoleEnum.USER, kakaoId);
}
userRepository.save(kakaoUser);
}
return kakaoUser;
}
}
User.entity로 가기 (User table)
package com.sparta.myselectshop.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
//카카오 아이디 필드 추가
private Long kakaoId;
public User(String username, String password, String email, UserRoleEnum role) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
}
//카카오 아이디랑 카카오 사용자 정보로 UserEntity 객체를 만드는 생성자 하나 추가
public User(String username, String password, String email, UserRoleEnum role, Long kakaoId) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
this.kakaoId =kakaoId;
}
//카카오 아이디를 업데이트하는 메소드
public User kakaoIdUpdate(Long kakaoId) {
this.kakaoId = kakaoId;
return this;
}
}
UserRepository
package com.sparta.myselectshop.repository;
import com.sparta.myselectshop.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
//추가
Optional<User> findByKakaoId(Long kakaoId);
}
kakaoService
package com.sparta.myselectshop.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.myselectshop.dto.KakaoUserInfoDto;
import com.sparta.myselectshop.entity.User;
import com.sparta.myselectshop.entity.UserRoleEnum;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.UUID;
@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate; //빨간 줄 : RestTemplate을 그냥 Builder로 만들 수 있다
//빨간줄 뜨는 이유 : restTemplate이 bean이 아닌 Rest Template Builder을 통해서 생성할 수 있도록 유도
//Bean을 수동으로 등록해서 관리하는 방법
private final JwtUtil jwtUtil;
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
//3. 필요 시에 회원 가입
User kakaoUser = registerKakaoUserIfNeeded(kakaoUserInfo); //카카오 사용자 정보
//4. JWT 토큰 반환
String createToken = jwtUtil.createToken(kakaoUser.getUsername(), kakaoUser.getRole());
return createToken;
}
//kakaoLogin에서 받은 인증 코드로 토큰 요청
private String getToken(String code) throws JsonProcessingException {
log.info("인가코드 : "+code);
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com") //여긴 정해진 주소를 넣기
.path("/oauth/token")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
//kakao Developers에서 요청한대로 http 형태를 만들어서 Rest template으로 API를 요청함
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "13f0c1acd35319458b5893b3dbba382d"); //여기에 RESTAPI 코드 다시 넣어주기
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code); //받은 인가코드 넣기
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //body가 있으니까 POST
.headers(headers)
.body(body);
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange( //http 요청이 카카오 서버 쪽으로 감
requestEntity,
String.class
); //ResponseEntity<String>에서 반환되는 String이 토큰의 형태로 되어 있음 > 그 토큰을 한번 파싱할 것임 (.readTree(response.getBody()))
// HTTP 응답 (JSON) -> 액세스 토큰 파싱 = 우리가 딱 원하는 액세스 토큰만 딱 뽑아옴
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
//뽑으면 .asText()니까 String값 반환
//맨 위의 String accessToken = getToken(code);의 String accessToken
}
//사용자 정보 요청 (getKakaoUserInfo 메서드)
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
log.info("accessToken : "+accessToken);
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://kapi.kakao.com")
.path("/v2/user/me")
.encode()
.build()
.toUri();
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri) //우리가 url 만든 거 보내주기
.headers(headers)
.body(new LinkedMultiValueMap<>()); //body는 따로 보내줄 필요가 없어서 new~
// HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class //String 타입으로 받아옴
);
//Long 타입으로 해당하는 id 받아오는 방법
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
//해댱하는 id값들을 받아올 수 있음
Long id = jsonNode.get("id").asLong();
//properties 중에서 nickname 값을 가져옴
String nickname = jsonNode.get("properties")
.get("nickname").asText();
//properties 중에서 email 값을 가져옴
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
//KakaoUserInfoDto에 값을 넣어줌
return new KakaoUserInfoDto(id, nickname, email);
}
private User registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId(); //카카오 아이디 가져오기
//user테이블에서 넣어준 kakaoId가 있으면 회원가입이 된거고 없으면 회원 가입을 한 적이 없는 것임 (없으면 .orElse (null) -> null이 들어감)
User kakaoUser = userRepository.findByKakaoId(kakaoId).orElse(null);
//회원가입 정보가 없어서 null이 뜨면 아래 회원가입 시켜주는 코드가 실행됨
if (kakaoUser == null) {
//카카오 소셜 로그인으로 로그인한 전적 없음
//우리 사이트에서 그냥 바로 회원가입 했으나, kakaoEmail로 회원가입한 사람
//근데 그 kakaoEmail로 카카오 로그인을 시도할 경우,
// 카카오 사용자 email 동일한 email 가진 회원이 있는지 확인
// 이 경우 같은 회원이라고 판단하자
String kakaoEmail = kakaoUserInfo.getEmail();
User sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if (sameEmailUser != null) { //카카오 이메일을 통해 일반 로그인한 유저 있음
kakaoUser = sameEmailUser;
// 기존 회원정보에 카카오 Id 추가
kakaoUser = kakaoUser.kakaoIdUpdate(kakaoId);
} else {
// 진짜 신규 카카오 회원가입
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password); //인코딩해서 암호화해서 넣어줌
// email: kakao email
String email = kakaoUserInfo.getEmail();
kakaoUser = new User(kakaoUserInfo.getNickname(), encodedPassword, email, UserRoleEnum.USER, kakaoId); //일반 사용자 권한
}
userRepository.save(kakaoUser); //상황에 따라 update 또는 save이기 때문에 @Transactional 걸어주지 않고 .save() 사용했음
}
return kakaoUser;
}
}
에러 나는 원인
쿠키 밸류는 공백이 들어가면 안됨 근데 Bearer 다음에 공백이 들어가면서 에러남
Controller 에서 substring 해주기
userController
package com.sparta.myselectshop.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sparta.myselectshop.dto.SignupRequestDto;
import com.sparta.myselectshop.dto.UserInfoDto;
import com.sparta.myselectshop.entity.UserRoleEnum;
import com.sparta.myselectshop.jwt.JwtUtil;
import com.sparta.myselectshop.security.UserDetailsImpl;
import com.sparta.myselectshop.service.FolderService;
import com.sparta.myselectshop.service.KakaoService;
import com.sparta.myselectshop.service.UserService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/api")
public class UserController {
private final UserService userService;
private final FolderService folderService;
private final KakaoService kakaoService;
@GetMapping("/user/login-page")
public String loginPage() {
return "login";
}
@GetMapping("/user/signup")
public String signupPage() {
return "signup";
}
@PostMapping("/user/signup")
public String signup(@Valid SignupRequestDto requestDto, BindingResult bindingResult) {
// Validation 예외처리
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
if (fieldErrors.size() > 0) {
for (FieldError fieldError : bindingResult.getFieldErrors()) {
log.error(fieldError.getField() + " 필드 : " + fieldError.getDefaultMessage());
}
return "redirect:/api/user/signup";
}
userService.signup(requestDto);
return "redirect:/api/user/login-page";
}
// 회원 관련 정보 받기
@GetMapping("/user-info")
@ResponseBody
public UserInfoDto getUserInfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
String username = userDetails.getUser().getUsername();
UserRoleEnum role = userDetails.getUser().getRole();
boolean isAdmin = (role == UserRoleEnum.ADMIN);
return new UserInfoDto(username, isAdmin);
}
@GetMapping("/user-folder")
public String getUserInfo(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
model.addAttribute("folders", folderService.getFolders(userDetails.getUser()));
return "index :: #fragment";
}
//카카오에서 보내는 인가 코드는 쿼리 스트링 방식으로 넘어오고, @RequestParam으로 받을 수 있음
//완료되면 이 jwt를 생성해서 쿠키를 우리가 직접 만들어서 브라우저에게 시킬 것임
//이전에 회원 기능 구현할 때는 header에 jwt 넣었음
//우리 client 구조 상 카카오 login 했을 때는 우리가 직접 쿠키를 생성해서 jwt에 넣은 다음에 브라우저에 자동으로 set될 수 있도록 구현해보기
@GetMapping("/user/kakao/callback")
public String kakaoLogin(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {
//kakaoService 단의 kakoLogin 메소드
String token = kakaoService.kakaoLogin(code); //String token = jwt token
//빨간 줄 : 예외를 시그니처에 추가 누르기
//kakao에서 받아온 token을 쿠키에 넣어주고 reponse 객체 넣어주는 작업
Cookie cookie = new Cookie(JwtUtil.AUTHORIZATION_HEADER, token.substring(7));
cookie.setPath("/");
response.addCookie(cookie); //브라우저의 jwt 값이 set 될 것임
return "redirect:/";
}
}