Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/bst/bobsoolting/config/KakaoProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package bst.bobsoolting.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "spring.security.oauth2.client.registration.kakao")
public class KakaoProperties {
private String clientId;
private String clientSecret;
private String redirectUri;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@Slf4j
@RequiredArgsConstructor
Expand Down Expand Up @@ -68,4 +70,23 @@ public ResponseEntity<ResponseProfileVO> updateProfile(@RequestBody RequestUpdat
ResponseProfileVO responseProfileVO = memberConverter.fromEntityToProfileVO(updatedMember);
return ResponseEntity.ok(responseProfileVO);
}

@PostMapping("/auth/kakao")
public ResponseEntity<?> kakaoLogin(@RequestBody Map<String, String> request) {
String code = request.get("code");
log.info("카카오 로그인 요청. 인가 코드: {}", code);
try {
String accessToken = memberCommandService.getKakaoAccessToken(code);
MemberDTO member = memberCommandService.getKakaoUserInfo(accessToken);
ResponseCreateMemberVO response = memberConverter.fromEntityToCreateVO(member);

return ResponseEntity.ok(response);
} catch (CommonException e) {
log.error("카카오 로그인 오류: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
} catch (Exception e) {
log.error("예상치 못한 오류 발생", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("카카오 로그인 처리 중 오류 발생");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@
import bst.bobsoolting.member.command.application.dto.MemberDTO;
import bst.bobsoolting.member.command.domain.vo.request.RequestAdditionalRegisterVO;
import bst.bobsoolting.member.command.domain.vo.request.RequestUpdateProfileVO;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.transaction.annotation.Transactional;


public interface MemberCommandService {
MemberDTO updateMemberProfile(String kakaoId, RequestUpdateProfileVO updateInfo);

MemberDTO updateMemberProfile(OAuth2User user, RequestUpdateProfileVO updateInfo);

@Transactional
String getKakaoAccessToken(String code);

MemberDTO getKakaoUserInfo(String accessToken);

@Transactional
MemberDTO createOrUpdateMember(String kakaoId, String nickname);

MemberDTO updateMemberAdditionalInfo(String kakaoId, RequestAdditionalRegisterVO info);

void createBasicMember(String kakaoId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import bst.bobsoolting.common.exception.CommonException;
import bst.bobsoolting.common.exception.ErrorCode;
import bst.bobsoolting.config.KakaoProperties;
import bst.bobsoolting.member.command.application.dto.MemberDTO;
import bst.bobsoolting.member.command.application.mapper.MemberConverter;
import bst.bobsoolting.member.command.domain.aggregate.MemberGender;
Expand All @@ -13,12 +14,19 @@
import bst.bobsoolting.member.query.repository.MemberMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import java.util.Map;
import java.util.Date;
import java.util.UUID;

Expand All @@ -30,16 +38,32 @@ public class MemberCommandServiceImpl implements MemberCommandService {
private final MemberRepository memberRepository;
private final MemberMapper memberMapper;
private final MemberConverter memberConverter;
private final KakaoProperties kakaoProperties;

@Override
@Transactional
public MemberDTO createMember(MemberDTO newMemberDTO) {
try {
String memberId = generateMemberId();
log.info("생성된 memberId: {}", memberId);

Member newMember = memberConverter.fromDTOToEntity(newMemberDTO, memberId);

memberRepository.save(newMember);
log.info("회원가입 완료: kakaoId={}, memberId={}", newMember.getKakaoId(), newMember.getMemberId());
return memberConverter.fromEntityToDTO(newMember);
} catch (Exception e) {
log.error("회원가입 실패: {}", e.getMessage(), e);
throw new CommonException(ErrorCode.INTERNAL_SERVER_ERROR);

public void createBasicMember(String kakaoId) {
log.info("신규 회원 기본 정보 저장 진행. kakaoId={}", kakaoId);

Member existingMember = memberMapper.findByKakaoId(kakaoId);
if (existingMember != null) {
log.warn("이미 존재하는 회원입니다. kakaoId={}", kakaoId);
throw new CommonException(ErrorCode.ALREADY_EXISTS);

}
Member newMember = createMember(kakaoId);
memberRepository.save(newMember);
Expand Down Expand Up @@ -92,6 +116,96 @@ public MemberDTO updateMemberProfile(String kakaoId, RequestUpdateProfileVO upda
return memberConverter.fromEntityToDTO(member);
}

@Override
@Transactional
public String getKakaoAccessToken(String code) {
String tokenUrl = "https://kauth.kakao.com/oauth/token";

RestTemplate restTemplate = new RestTemplate();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", kakaoProperties.getClientId());
body.add("redirect_uri", kakaoProperties.getRedirectUri());
body.add("code", code);
body.add("client_secret", kakaoProperties.getClientSecret());

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<Map> response = restTemplate.exchange(tokenUrl, HttpMethod.POST, request, Map.class);

if (response.getStatusCode() != HttpStatus.OK || response.getBody() == null) {
throw new CommonException(ErrorCode.INTERNAL_SERVER_ERROR);
}

String accessToken = (String) response.getBody().get("access_token");
if (accessToken == null) {
throw new CommonException(ErrorCode.INVALID_REQUEST_BODY);
}

return accessToken;
}

@Override
@Transactional
public MemberDTO getKakaoUserInfo(String accessToken) {
String userInfoUrl = "https://kapi.kakao.com/v2/user/me";

RestTemplate restTemplate = new RestTemplate();

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);

HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<Map> response = restTemplate.exchange(userInfoUrl, HttpMethod.GET, request, Map.class);

if (response.getStatusCode() != HttpStatus.OK || response.getBody() == null) {
throw new CommonException(ErrorCode.INTERNAL_SERVER_ERROR);
}

Map<String, Object> responseBody = response.getBody();
Map<String, Object> kakaoAccount = (Map<String, Object>) responseBody.get("kakao_account");

String kakaoId = String.valueOf(responseBody.get("id"));
String nickname = (String) ((Map<String, Object>) kakaoAccount.get("profile")).get("nickname");

return createOrUpdateMember(kakaoId, nickname);
}

@Override
@Transactional
public MemberDTO createOrUpdateMember(String kakaoId, String nickname) {
Member existingMember = memberMapper.findByKakaoId(kakaoId);

if (existingMember == null) {
String memberId = generateMemberId();
Member newMember = Member.builder()
.memberId(memberId)
.kakaoId(kakaoId)
.nickname(nickname)
.profileImage(null)
.gender(null)
.birth(null)
.university(null)
.department(null)
.studentNumber(0)
.rating(0.0f)
.memberRole(null)
.build();

memberRepository.save(newMember);
log.info("신규 회원 생성: kakaoId={}, memberId={}", kakaoId, memberId);
return memberConverter.fromEntityToDTO(newMember);
} else {
existingMember.setNickname(nickname);

memberRepository.save(existingMember);
log.info("기존 회원 정보 업데이트: kakaoId={}, memberId={}", kakaoId, existingMember.getMemberId());
return memberConverter.fromEntityToDTO(existingMember);
}

private Member createMember(String kakaoId) {
Member newMember = Member.builder()
.memberId(generateMemberId())
Expand All @@ -110,6 +224,7 @@ private Member createMember(String kakaoId) {
.updatedAt(LocalDateTime.now())
.build();
return newMember;

}

private String generateMemberId() {
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ spring:
client-secret: ${KAKAO_CLIENT_SECRET_KEY}
client-name: kakao
authorization-grant-type: authorization_code
redirect-uri: ${KAKAO_BASE_URL}/login/oauth2/code/kakao
redirect-uri: http://localhost:3000/login
# redirect-uri: ${KAKAO_BASE_URL}/login/oauth2/code/kakao
scope:
- profile_nickname
- profile_image
Expand Down
Loading