diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2bb2e6b..4fe9f8c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -56,10 +56,10 @@ jobs: - name: Upload to AWS S3 run: | aws deploy push \ - --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} \ - --ignore-hidden-files \ - --s3-location s3://$AWS_S3_BUCKET/cicdtest/$GITHUB_SHA.zip \ - --source . + --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} \ + --ignore-hidden-files \ + --s3-location s3://$AWS_S3_BUCKET/cicdtest/$GITHUB_SHA.zip \ + --source . #업로드된 ZIP 파일을 CodeDeploy로 배포 - name: AWS Code Deploy run: | diff --git a/appspec.yml b/appspec.yml index b62a5f6..bd3794d 100644 --- a/appspec.yml +++ b/appspec.yml @@ -4,7 +4,7 @@ os: linux files: - source: / # 인스턴스에 복사할 디렉터리 경로 destination: /home/ec2-user/mapddang-back # 인스턴스에서 파일이 복사되는 위치 - overwrite: yes # 복사할 위치에 파일이 있는 경우 대체 + overwrite: true # 복사할 위치에 파일이 있는 경우 대체 permissions: - object: / # 권한이 지정되는 파일 or 디렉터리 @@ -18,4 +18,4 @@ hooks: ApplicationStart: # CodeDeploy의 ApplicationStart 단계에서 실행 - location: scripts/start.sh - timeout: 60 \ No newline at end of file + timeout: 60 diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java b/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java index cd01533..3074034 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java +++ b/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java @@ -3,17 +3,18 @@ import com.dnd.dndtravel.auth.controller.request.AppleWithdrawRequest; import com.dnd.dndtravel.auth.controller.request.ReIssueTokenRequest; import com.dnd.dndtravel.auth.controller.swagger.AuthControllerSwagger; +import com.dnd.dndtravel.auth.service.TokenDecoder; import com.dnd.dndtravel.auth.service.dto.response.AppleIdTokenPayload; import com.dnd.dndtravel.auth.service.AppleOAuthService; import com.dnd.dndtravel.auth.service.JwtTokenService; import com.dnd.dndtravel.auth.controller.request.AppleLoginRequest; +import com.dnd.dndtravel.auth.service.dto.response.AppleSocialTokenInfoResponse; import com.dnd.dndtravel.auth.service.dto.response.TokenResponse; import com.dnd.dndtravel.auth.service.dto.response.ReissueTokenResponse; import com.dnd.dndtravel.config.AuthenticationMember; import com.dnd.dndtravel.member.domain.Member; import com.dnd.dndtravel.member.service.MemberService; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -29,13 +30,14 @@ public class AuthController implements AuthControllerSwagger { @PostMapping("/login/oauth2/apple") public ResponseEntity appleOAuthLogin(@RequestBody AppleLoginRequest appleLoginRequest) { // 클라이언트에서 준 code 값으로 apple의 IdToken Payload를 얻어온다 - AppleIdTokenPayload tokenPayload = appleOAuthService.get(appleLoginRequest.appleToken()); + AppleSocialTokenInfoResponse tokenInfo = appleOAuthService.getTokenInfo(appleLoginRequest.appleToken()); + AppleIdTokenPayload tokenPayload = TokenDecoder.decodePayload(tokenInfo.idToken(), AppleIdTokenPayload.class); // apple에서 가져온 유저정보를 DB에 저장 Member member = memberService.saveMember(tokenPayload.email(), appleLoginRequest.selectedColor()); // 클라이언트와 주고받을 user token(access , refresh) 생성 - TokenResponse tokenResponse = jwtTokenService.generateTokens(member.getId()); + TokenResponse tokenResponse = jwtTokenService.generateTokens(member.getId(), tokenInfo.refreshToken()); // refresh token 재발급 필요시 if (tokenResponse == null) { @@ -51,14 +53,11 @@ public ReissueTokenResponse reissueToken(@RequestBody ReIssueTokenRequest reissu } @DeleteMapping("/withdraw") - public void withdraw(@Valid @RequestBody AppleWithdrawRequest withdrawRequest, AuthenticationMember authenticationMember) { - // 1. Apple 서버에서 Access Token 받아오기 - String accessToken = appleOAuthService.getAccessToken(withdrawRequest.authorizationCode()); + public void withdraw(@RequestBody AppleWithdrawRequest withdrawRequest, AuthenticationMember authenticationMember) { + // 애플 서버에 탈퇴 요청 + appleOAuthService.revoke(withdrawRequest.appleRefreshToken()); - // 2. Apple 서버에 탈퇴 요청 - appleOAuthService.revoke(accessToken); - - // 3. 자체 회원 탈퇴 진행 + // 자체 회원 탈퇴 memberService.withdrawMember(authenticationMember.id()); } } \ No newline at end of file diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java index 2966afc..7bdeea3 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java +++ b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java @@ -10,9 +10,9 @@ 클라이언트에서 서버로 보내는 요청 */ public record AppleWithdrawRequest( - @Schema(description = "authorization code", requiredMode = REQUIRED) - @NotBlank(message = "authorization code는 필수 입니다.") - @Size(max = 300, message = "authorization code 형식이 아닙니다.") - String authorizationCode + @Schema(description = "Apple Refresh Token", requiredMode = REQUIRED) + @NotBlank(message = "appleRefreshToken은 필수 입니다.") + @Size(max = 300, message = "token 형식이 아닙니다.") + String appleRefreshToken ) { } diff --git a/src/main/java/com/dnd/dndtravel/auth/repository/RefreshTokenRepository.java b/src/main/java/com/dnd/dndtravel/auth/repository/RefreshTokenRepository.java index 83c7996..b9e899a 100644 --- a/src/main/java/com/dnd/dndtravel/auth/repository/RefreshTokenRepository.java +++ b/src/main/java/com/dnd/dndtravel/auth/repository/RefreshTokenRepository.java @@ -5,11 +5,9 @@ import com.dnd.dndtravel.auth.domain.RefreshToken; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -@Repository public interface RefreshTokenRepository extends JpaRepository { RefreshToken findByMemberId(Long memberId); - + void deleteByMemberId(Long memberId); Optional findByRefreshToken(String refreshToken); } \ No newline at end of file diff --git a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java index d0372ed..9cd4c19 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java @@ -48,17 +48,6 @@ public class AppleOAuthService { private final AppleClient appleClient; private final AppleProperties appleProperties; - public AppleIdTokenPayload get(String authorizationCode) { - String idToken = appleClient.getIdToken( - appleProperties.getClientId(), - generateClientSecret(), - appleProperties.getGrantType(), - authorizationCode - ).idToken(); - - return TokenDecoder.decodePayload(idToken, AppleIdTokenPayload.class); - } - private String generateClientSecret() { LocalDateTime expiration = LocalDateTime.now().plusMinutes(5); @@ -87,23 +76,22 @@ private PrivateKey getPrivateKey() { } } - public String getAccessToken(String authorizationCode) { - AppleSocialTokenInfoResponse tokenInfo = appleClient.getIdToken( + public AppleSocialTokenInfoResponse getTokenInfo(String authorizationCode) { + return appleClient.getIdToken( appleProperties.getClientId(), generateClientSecret(), appleProperties.getGrantType(), authorizationCode ); - return tokenInfo.accessToken(); } - public void revoke(String accessToken) { + public void revoke(String refreshToken) { try { appleClient.revoke( appleProperties.getClientId(), generateClientSecret(), - accessToken, - "access_token" + refreshToken, + "refresh_token" ); } catch (Exception e) { throw new AppleTokenRevokeException(e); diff --git a/src/main/java/com/dnd/dndtravel/auth/service/JwtTokenService.java b/src/main/java/com/dnd/dndtravel/auth/service/JwtTokenService.java index 14f18bb..77e2fdf 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/JwtTokenService.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/JwtTokenService.java @@ -18,14 +18,14 @@ public class JwtTokenService { private final RefreshTokenRepository refreshTokenRepository; @Transactional - public TokenResponse generateTokens(Long memberId) { + public TokenResponse generateTokens(Long memberId, String appleRefreshToken) { RefreshToken refreshToken = refreshTokenRepository.findByMemberId(memberId); // 리프레시 토큰이 없는경우 if (refreshToken == null) { String newRefreshToken = jwtProvider.refreshToken(); refreshTokenRepository.save(RefreshToken.of(memberId, newRefreshToken)); // refreshToken은 DB에 저장 - return new TokenResponse(jwtProvider.accessToken(memberId), newRefreshToken); + return new TokenResponse(jwtProvider.accessToken(memberId), newRefreshToken, appleRefreshToken); } // 리프레시 토큰이 만료됐으면 재발급 받으라고 멘트줌 @@ -37,7 +37,7 @@ public TokenResponse generateTokens(Long memberId) { refreshTokenRepository.delete(refreshToken); String newRefreshToken = jwtProvider.refreshToken(); refreshTokenRepository.save(RefreshToken.of(refreshToken.getMemberId(), newRefreshToken)); - return new TokenResponse(jwtProvider.accessToken(memberId), newRefreshToken); + return new TokenResponse(jwtProvider.accessToken(memberId), newRefreshToken, appleRefreshToken); } @Transactional diff --git a/src/main/java/com/dnd/dndtravel/auth/service/dto/response/TokenResponse.java b/src/main/java/com/dnd/dndtravel/auth/service/dto/response/TokenResponse.java index e1c096b..83c2d84 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/dto/response/TokenResponse.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/dto/response/TokenResponse.java @@ -2,6 +2,7 @@ public record TokenResponse( String accessToken, - String refreshToken + String refreshToken, + String appleRefreshToken ) { } \ No newline at end of file diff --git a/src/main/java/com/dnd/dndtravel/config/SwaggerConfig.java b/src/main/java/com/dnd/dndtravel/config/SwaggerConfig.java index da3df51..d4037fa 100644 --- a/src/main/java/com/dnd/dndtravel/config/SwaggerConfig.java +++ b/src/main/java/com/dnd/dndtravel/config/SwaggerConfig.java @@ -16,7 +16,7 @@ public class SwaggerConfig { public OpenAPI openAPI() { return new OpenAPI() .info(new Info() - .title("MAPDDANG API") + .title("MAPDDANG TEST API") .description("맵땅 앱 관련 API") .version("1.0.0")) .addServersItem(new Server().url("/").description("Generated Default Server URL")) //local에서는 http, aws에서는 https로 server url 설정됨 diff --git a/src/main/java/com/dnd/dndtravel/map/repository/MemberAttractionRepository.java b/src/main/java/com/dnd/dndtravel/map/repository/MemberAttractionRepository.java index 8dc78fe..528c907 100644 --- a/src/main/java/com/dnd/dndtravel/map/repository/MemberAttractionRepository.java +++ b/src/main/java/com/dnd/dndtravel/map/repository/MemberAttractionRepository.java @@ -9,10 +9,10 @@ import com.dnd.dndtravel.map.repository.custom.MemberAttractionRepositoryCustom; public interface MemberAttractionRepository extends JpaRepository, - MemberAttractionRepositoryCustom { + MemberAttractionRepositoryCustom { - List findByMemberId(long memberId); - - Optional findByIdAndMemberId(long memberAttractionId, long memberId); + List findByMemberId(long memberId); + void deleteByMemberId(Long memberId); + Optional findByIdAndMemberId(long memberAttractionId, long memberId); } diff --git a/src/main/java/com/dnd/dndtravel/map/repository/MemberRegionRepository.java b/src/main/java/com/dnd/dndtravel/map/repository/MemberRegionRepository.java index f392f16..26f8bba 100644 --- a/src/main/java/com/dnd/dndtravel/map/repository/MemberRegionRepository.java +++ b/src/main/java/com/dnd/dndtravel/map/repository/MemberRegionRepository.java @@ -8,6 +8,6 @@ public interface MemberRegionRepository extends JpaRepository { List findByMemberId(Long memberId); - + void deleteByMemberId(Long memberId); MemberRegion findByMemberIdAndRegionId(Long memberId, Long regionId); } diff --git a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java index 718f672..5a500aa 100644 --- a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java +++ b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java @@ -1,6 +1,7 @@ package com.dnd.dndtravel.member.service; import com.dnd.dndtravel.auth.repository.RefreshTokenRepository; +import com.dnd.dndtravel.map.exception.MemberNotFoundException; import com.dnd.dndtravel.map.repository.MemberAttractionRepository; import com.dnd.dndtravel.map.repository.MemberRegionRepository; import com.dnd.dndtravel.auth.service.MemberNameGenerator; @@ -19,8 +20,8 @@ public class MemberService { private final MemberRepository memberRepository; private final MemberAttractionRepository memberAttractionRepository; private final MemberRegionRepository memberRegionRepository; - private final RefreshTokenRepository refreshTokenRepository; private final MemberNameGenerator memberNameGenerator; + private final RefreshTokenRepository refreshTokenRepository; @Transactional public Member saveMember(String email, String selectedColor) { @@ -32,12 +33,12 @@ public Member saveMember(String email, String selectedColor) { @Transactional public void withdrawMember(long memberId) { Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new RuntimeException("Member not found")); + .orElseThrow(() -> new MemberNotFoundException(memberId)); + memberAttractionRepository.deleteByMemberId(memberId); + memberRegionRepository.deleteByMemberId(memberId); + refreshTokenRepository.deleteByMemberId(memberId); memberRepository.delete(member); - memberAttractionRepository.deleteById(memberId); - memberRegionRepository.deleteById(memberId); - refreshTokenRepository.deleteById(memberId); } @Transactional(readOnly = true) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f3cdbac..23f33d9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -47,5 +47,4 @@ cloud: stack: auto: false s3: - bucketName: ${AWS_BUCKET_NAME} - + bucketName: ${AWS_BUCKET_NAME} \ No newline at end of file