Service
MemberService.java
package code.code_api.service;
import code.code_api.domain.CodeMember;
import code.code_api.dto.MemberDTO;
import code.code_api.dto.MemberModifyDTO;
import java.util.stream.Collectors;
public interface MemberService {
MemberDTO getKakaoMember(String accessToken);
void modifyMember(MemberModifyDTO memberModifyDTO);
//엔터티 > DTO 변환
default MemberDTO entityToDTO(CodeMember member){
MemberDTO memberDTO = new MemberDTO(
member.getEmail(),
member.getNickname(),
member.getNickname(),
member.isSocial(),
member.getMemberRoleList().stream().map(memberRole ->
memberRole.name()).collect(Collectors.toList())
);
return memberDTO;
}
}
MemberServiceImpl.java
package code.code_api.service;
import code.code_api.domain.CodeMember;
import code.code_api.domain.MemberRole;
import code.code_api.dto.KakaoUserInfoDTO;
import code.code_api.dto.MemberDTO;
import code.code_api.dto.MemberModifyDTO;
import code.code_api.repository.CodeMemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.LinkedHashMap;
import java.util.Optional;
@Service
@Slf4j
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService{
private final CodeMemberRepository codeMemberRepository;
private final PasswordEncoder passwordEncoder;
//accessToken을 이용해서 사용자 정보 가져오기 => 기존 회원 정보가 있는 경우, 없는 경우
@Override
public MemberDTO getKakaoMember(String accessToken) {
KakaoUserInfoDTO kakaoUserInfo = getNicknameFromKakaoAccessToken(accessToken);
log.info("가져온 nickName {}", kakaoUserInfo);
Optional<CodeMember> result = codeMemberRepository.findByNickname(kakaoUserInfo.getNickname());
//기존 회원의 경우 DTO로 변환한 뒤 반환
if(result.isPresent()){
MemberDTO memberDTO = entityToDTO(result.get());
return memberDTO;
}
//새로운 회원의 경우 비밀번호 임의로 생성
CodeMember socialMember = makeSocialMember(kakaoUserInfo.getId(), kakaoUserInfo.getNickname());
codeMemberRepository.save(socialMember);
MemberDTO memberDTO = entityToDTO(socialMember);
return memberDTO;
}
private KakaoUserInfoDTO getNicknameFromKakaoAccessToken(String accessToken) {
String kakaoGetUserURL = "https://kapi.kakao.com/v2/user/me";
//토큰이 없을 경우
if(accessToken == null) {
throw new RuntimeException("AccessToken is null");
}
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
HttpEntity<Object> entity = new HttpEntity<>(headers);
//실제로 보내야 한다. UriComponentsBuilder를 사용한다.
UriComponents uriBuilder = UriComponentsBuilder.fromHttpUrl(kakaoGetUserURL).build();
//이 때 나오는 정보가 LinkedHashMap 형태로 나온다.
ResponseEntity<LinkedHashMap> response = restTemplate.exchange(
uriBuilder.toString(),
HttpMethod.GET,
entity,
LinkedHashMap.class
);
log.info("response {}", response);
//사용자 정보에서 아이디를 가져오자, nickName, id
LinkedHashMap<String, LinkedHashMap> bodyMap = response.getBody();
log.info("bodyMap {}", bodyMap);
log.info("id {}", bodyMap.get("id"));
String id = String.valueOf(bodyMap.get("id"));
LinkedHashMap<String, String> properties = bodyMap.get("properties");
log.info("nickName {}", properties.get("nickname"));
String nickname = properties.get("nickname");
return new KakaoUserInfoDTO(id, nickname);
}
//해당 닉네임을 가진 회원이 없다면 새로운 회원을 추가할 때 패스워드를 임의로 생성한다.
private String makeTempPassword() {
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < 10; i++) {
buffer.append( (char) ( (int)(Math.random() * 55) + 65 ));
}
return buffer.toString();
}
//소셜 회원 만들기
private CodeMember makeSocialMember(String id, String nickname){
String tempPassword = makeTempPassword();
log.info("tempPassword: ", tempPassword);
//회원만들기
CodeMember member = CodeMember.builder()
.email(id + "@email.com")
.pw(passwordEncoder.encode(tempPassword))
.nickname(nickname)
.social(true)
.build();
member.addRole(MemberRole.USER);
return member;
}
//회원 수정
@Override
public void modifyMember(MemberModifyDTO memberModifyDTO) {
Optional<CodeMember> result = codeMemberRepository.findByNickname(memberModifyDTO.getNickName());
CodeMember member = result.orElseThrow();
member.setPw(passwordEncoder.encode(memberModifyDTO.getPw()));
member.setSocial(false);
member.setNickname(memberModifyDTO.getNickName());
codeMemberRepository.save(member);
}
}
Controller
SocialController.java
package code.code_api.controller;
import code.code_api.dto.MemberDTO;
import code.code_api.dto.MemberModifyDTO;
import code.code_api.service.MemberService;
import code.code_api.util.JWTUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@Slf4j
@RequiredArgsConstructor
public class SocialController {
private final MemberService memberService;
@GetMapping("/api/member/kakao")
public Map<String, Object> getMemberKakao(@RequestParam("accessToken") String accessToken) {
log.info("react에서 가져온 accessToken {}", accessToken);
MemberDTO memberDTO = memberService.getKakaoMember(accessToken);
Map<String, Object> claims = memberDTO.getClaims();
String jwtAccessToken = JWTUtil.generateToken(claims, 10);
String jwtRefreshToken = JWTUtil.generateToken(claims, 60 * 24);
claims.put("accessToken", jwtAccessToken);
claims.put("refreshToken", jwtRefreshToken);
return claims;
}
@PutMapping("/api/member/modify")
public Map<String, String> modify(@RequestBody MemberModifyDTO memberModifyDTO) {
memberService.modifyMember(memberModifyDTO);
return Map.of("RESULT", "modified");
}
}
DTO
KakaoUserInfoDTO.java
package code.code_api.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
public class KakaoUserInfoDTO {
private String id;
private String nickname;
}
MemberModifyDTO.java
package code.code_api.dto;
import lombok.Data;
@Data
public class MemberModifyDTO {
private String email;
private String pw;
private String nickName;
}
Repository
CodeMemberRepository.java
package code.code_api.repository;
import code.code_api.domain.CodeMember;
import org.aspectj.apache.bcel.classfile.Code;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Optional;
public interface CodeMemberRepository extends JpaRepository<CodeMember, String> {
@EntityGraph(attributePaths = {"memberRoleList"})
@Query("select m from CodeMember m where m.email = :email")
CodeMember getWithRoles(@Param("email") String email);
//nickname으로 찾기
Optional<CodeMember> findByNickname(String nickname);
}
Test
CodeMemberRepositoryTest.java
@Test
void 회원삭제() {
String email = "3846973570@email.com";
CodeMember member = codeMemberRepository.getWithRoles(email);
if(member == null){
log.info("회원이 존재하지 않습니다. 이메일 : {}", email);
return;
}
//회원 삭제
codeMemberRepository.delete(member);
log.info("회원 삭제 완료 {}", email);
}
'Framework > Spring' 카테고리의 다른 글
| [Spring] 시큐리티 로그인 로직 (2) | 2024.12.18 |
|---|---|
| [Spring] 코드 프로젝트 (0) | 2024.12.05 |
| [Spring] 시큐어 코딩 예제 (0) | 2024.11.22 |
| [Spring] 문의 페이지 테스트 (0) | 2024.11.13 |
| [Spring] 에러 페이지 예제 (0) | 2024.11.12 |