Skip to content

Commit 5f41659

Browse files
authored
Merge pull request #247 from 7-umc-GrowIT/feat/#245
[#245] Feat: 로그아웃 api 구현
2 parents 0b68261 + c0761d2 commit 5f41659

8 files changed

Lines changed: 82 additions & 5 deletions

File tree

src/main/java/umc/GrowIT/Server/converter/UserConverter.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package umc.GrowIT.Server.converter;
22

33
import umc.GrowIT.Server.domain.User;
4+
import umc.GrowIT.Server.web.dto.AuthDTO.AuthResponseDTO;
45
import umc.GrowIT.Server.web.dto.OAuthDTO.OAuthApiResponseDTO;
56
import umc.GrowIT.Server.web.dto.OAuthDTO.OAuthResponseDTO;
67
import umc.GrowIT.Server.web.dto.TokenDTO.TokenResponseDTO;
@@ -50,4 +51,11 @@ public static OAuthResponseDTO.KakaoLoginDTO toKakaoLoginDTO(Boolean signupRequi
5051
.tokens(tokens)
5152
.build();
5253
}
54+
55+
public static AuthResponseDTO.LogoutResponseDTO toLogoutDTO(){
56+
57+
return AuthResponseDTO.LogoutResponseDTO.builder()
58+
.message("로그아웃이 완료되었습니다.")
59+
.build();
60+
}
5361
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package umc.GrowIT.Server.service.authService;
22

3-
import jakarta.validation.Valid;
43
import umc.GrowIT.Server.domain.enums.AuthType;
54
import umc.GrowIT.Server.web.dto.AuthDTO.AuthRequestDTO;
65
import umc.GrowIT.Server.web.dto.AuthDTO.AuthResponseDTO;
@@ -9,4 +8,6 @@ public interface AuthService {
98
AuthResponseDTO.SendAuthEmailResponseDTO sendAuthEmail(AuthType type, AuthRequestDTO.SendAuthEmailRequestDTO request);
109

1110
AuthResponseDTO.VerifyAuthCodeResponseDTO verifyAuthCode(AuthRequestDTO.VerifyAuthCodeRequestDTO request);
11+
12+
AuthResponseDTO.LogoutResponseDTO logout(Long userId);
1213
}

src/main/java/umc/GrowIT/Server/service/authService/AuthServiceImpl.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@
88
import org.apache.commons.lang3.RandomStringUtils;
99
import org.springframework.mail.javamail.JavaMailSender;
1010
import org.springframework.stereotype.Service;
11+
import org.springframework.transaction.annotation.Transactional;
1112
import org.thymeleaf.TemplateEngine;
1213
import org.thymeleaf.context.Context;
1314
import umc.GrowIT.Server.apiPayload.code.status.ErrorStatus;
1415
import umc.GrowIT.Server.apiPayload.exception.AuthHandler;
1516
import umc.GrowIT.Server.apiPayload.exception.UserHandler;
1617
import umc.GrowIT.Server.converter.AuthenticationCodeConverter;
18+
import umc.GrowIT.Server.converter.UserConverter;
1719
import umc.GrowIT.Server.domain.AuthenticationCode;
20+
import umc.GrowIT.Server.domain.RefreshToken;
1821
import umc.GrowIT.Server.domain.User;
1922
import umc.GrowIT.Server.domain.enums.AuthType;
2023
import umc.GrowIT.Server.domain.enums.CodeStatus;
2124
import umc.GrowIT.Server.repository.AuthenticationCodeRepository;
2225
import umc.GrowIT.Server.repository.UserRepository;
26+
import umc.GrowIT.Server.service.refreshTokenService.RefreshTokenCommandService;
2327
import umc.GrowIT.Server.service.userService.UserCommandService;
2428
import umc.GrowIT.Server.web.dto.AuthDTO.AuthRequestDTO;
2529
import umc.GrowIT.Server.web.dto.AuthDTO.AuthResponseDTO;
@@ -29,8 +33,6 @@
2933

3034
import org.springframework.beans.factory.annotation.Value;
3135

32-
import static umc.GrowIT.Server.domain.enums.UserStatus.INACTIVE;
33-
3436
@Service
3537
@RequiredArgsConstructor
3638
public class AuthServiceImpl implements AuthService {
@@ -40,6 +42,7 @@ public class AuthServiceImpl implements AuthService {
4042
private final JavaMailSender javaMailSender;
4143
private final TemplateEngine templateEngine;
4244
private final AuthenticationCodeRepository authenticationCodeRepository;
45+
private final RefreshTokenCommandService refreshTokenCommandService;
4346

4447
@Value("${spring.mail.username}")
4548
private String fromEmail;
@@ -149,4 +152,30 @@ private AuthenticationCode saveAuthenticationCode(String email, String authentic
149152
AuthenticationCode newAuthenticationCode = AuthenticationCodeConverter.toAuthenticationCode(email, authenticationCode);
150153
return authenticationCodeRepository.save(newAuthenticationCode);
151154
}
155+
156+
@Override
157+
@Transactional
158+
public AuthResponseDTO.LogoutResponseDTO logout(Long userId){
159+
160+
// 1. 사용자 조회
161+
User user = userRepository.findById(userId)
162+
.orElseThrow(()-> new UserHandler(ErrorStatus.USER_NOT_FOUND));
163+
164+
// 2. RefreshToken이 있는 경우에만 삭제 처리
165+
if (user.getRefreshToken() != null) {
166+
// RefreshToken DB에서 삭제
167+
RefreshToken refreshToken = user.getRefreshToken();
168+
user.deleteRefreshToken(); // User 엔티티의 refreshToken 필드를 null로 설정
169+
userRepository.save(user);
170+
171+
// RefreshToken 엔티티 삭제
172+
refreshTokenCommandService.deleteRefreshToken(refreshToken);
173+
}
174+
175+
// 3. 성공 응답 반환 (이미 로그아웃된 상태여도 성공으로 처리)
176+
return UserConverter.toLogoutDTO();
177+
178+
179+
}
180+
152181
}

src/main/java/umc/GrowIT/Server/service/refreshTokenService/RefreshTokenCommandService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public interface RefreshTokenCommandService {
1313

1414
TokenResponseDTO.AccessTokenDTO reissueToken(TokenRequestDTO.ReissueDTO reissueDTO);
1515

16-
16+
void deleteRefreshToken(RefreshToken refreshToken);
1717
}

src/main/java/umc/GrowIT/Server/service/refreshTokenService/RefreshTokenCommandServiceImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,9 @@ public TokenResponseDTO.AccessTokenDTO reissueToken(TokenRequestDTO.ReissueDTO r
6666

6767
}
6868

69+
@Override
70+
@Transactional
71+
public void deleteRefreshToken(RefreshToken refreshToken) {
72+
refreshTokenRepository.delete(refreshToken);
73+
}
6974
}

src/main/java/umc/GrowIT/Server/web/controller/AuthController.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import jakarta.validation.Valid;
55
import lombok.RequiredArgsConstructor;
66
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.security.core.Authentication;
8+
import org.springframework.security.core.context.SecurityContextHolder;
79
import org.springframework.web.bind.annotation.*;
810
import umc.GrowIT.Server.apiPayload.ApiResponse;
911
import umc.GrowIT.Server.domain.enums.AuthType;
@@ -92,4 +94,17 @@ public ApiResponse<TokenResponseDTO.TokenDTO> signupSocial(@RequestBody @Valid O
9294
TokenResponseDTO.TokenDTO tokenDTO = oAuthService.signupSocial(oAuthUserInfoAndUserTermsDTO);
9395
return ApiResponse.onSuccess(tokenDTO);
9496
}
97+
@Override
98+
@PostMapping("/logout")
99+
public ApiResponse<AuthResponseDTO.LogoutResponseDTO> logout() {
100+
// AccessToken에서 userId 추출
101+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
102+
Long userId = (Long) authentication.getPrincipal();
103+
104+
// 로그아웃 처리
105+
AuthResponseDTO.LogoutResponseDTO result = authService.logout(userId);
106+
107+
return ApiResponse.onSuccess(result);
108+
}
109+
95110
}

src/main/java/umc/GrowIT/Server/web/controller/specification/AuthSpecification.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import umc.GrowIT.Server.web.dto.TokenDTO.TokenRequestDTO;
1717
import umc.GrowIT.Server.web.dto.TokenDTO.TokenResponseDTO;
1818
import umc.GrowIT.Server.web.dto.UserDTO.UserRequestDTO;
19-
import umc.GrowIT.Server.web.dto.UserDTO.UserResponseDTO;
2019

2120
public interface AuthSpecification {
2221

@@ -100,4 +99,16 @@ ApiResponse<AuthResponseDTO.SendAuthEmailResponseDTO> sendAuthEmail(
10099
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TERM4003", description = "❌ 전체 약관 정보를 주세요.", content = @Content(schema = @Schema(implementation = ApiResponse.class)))
101100
})
102101
ApiResponse<TokenResponseDTO.TokenDTO> signupSocial(@RequestBody @Valid OAuthRequestDTO.OAuthUserInfoAndUserTermsDTO oAuthUserInfoAndUserTermsDTO);
102+
103+
@PostMapping("/logout")
104+
@Operation(summary = "로그아웃 API", description = "사용자 로그아웃을 처리하는 API입니다. AccessToken을 무효화하고 RefreshToken을 삭제합니다.")
105+
@ApiResponses({
106+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "⭕ SUCCESS"),
107+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "USER4002", description = "❌ 이메일 또는 패스워드가 일치하지 않습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
108+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "USER4004", description = "❌ 탈퇴한 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
109+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH4011", description = "❌ 유효하지 않은 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
110+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH4012", description = "❌ 만료된 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
111+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH4015", description = "❌ 토큰이 제공되지 않았습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class)))
112+
})
113+
ApiResponse<AuthResponseDTO.LogoutResponseDTO> logout();
103114
}

src/main/java/umc/GrowIT/Server/web/dto/AuthDTO/AuthResponseDTO.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,12 @@ public static class VerifyAuthCodeResponseDTO {
3131
// TODO 디테일하게 결정 필요
3232
private String message; // ex) 인증이 완료되었습니다, 인증번호가 올바르지 않습니다
3333
}
34+
35+
@Getter
36+
@Builder
37+
@NoArgsConstructor
38+
@AllArgsConstructor
39+
public static class LogoutResponseDTO {
40+
private String message; // "로그아웃이 완료되었습니다."
41+
}
3442
}

0 commit comments

Comments
 (0)