diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 943266e..71cd48c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,6 +9,6 @@ - [ ] -## 주의사항 +## 관련 이슈 사항 - Closes #{이슈 번호} \ No newline at end of file diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 47b9210..50c399c 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -12,10 +12,6 @@ on: - feature/* # paths: './**' - # defaults: - # run: - # working-directory: hibit-backend-improved - # Report 결과 쓰기 위해 필요 permissions: checks: write @@ -23,22 +19,21 @@ permissions: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: - GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} - GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }} + KAKAO_CLIENT_SECRET: ${{ secrets.KAKAO_CLIENT_SECRET }} JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} steps: - name: 레포지토리를 가져옵니다 uses: actions/checkout@v3 with: - token: ${{ secrets.HIBIT_IMPROVED_TOKEN}} - submodules: true + token: ${{ secrets.HIBIT_CI_TOKEN}} - - name: JDK 11을 설치합니다. + - name: JDK 21을 설치합니다. uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '21' distribution: 'temurin' - name: 타입존을 설정합니다 diff --git a/.gitignore b/.gitignore index 4e45262..467a2e1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,25 @@ build/ !**/src/main/**/build/ !**/src/test/**/build/ -application-prod.yml +### AWS ### +**/docker-compose-live.yml + +### Application ### +**/application-staging.yml +**/application-live.yml +**/application-prod.yml .env +### Logging ### +**/logback-staging.xml +**/logback-live.xml + +### Monitoring ### +**/prometheus-staging.yml +**/prometheus-live.yml +**/promtail-config-staging.yml +**/promtail-config-live.yml + ### STS ### .apt_generated .classpath diff --git a/build.gradle b/build.gradle index 865533b..300a4c1 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,10 @@ ext { // (5) 문서 조각들에 대한 경로를 지정 snippetsDir = file('build/generated-snippets') } +tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:deprecation" +} + tasks.named('bootBuildImage') { builder = 'paketobuildpacks/builder-jammy-base:latest' } @@ -86,26 +90,26 @@ asciidoctor { jacoco { // JaCoCo 버전 - toolVersion = '0.8.7' + toolVersion = "${jacocoVersion}" } jacocoTestReport { reports { // 원하는 리포트를 켜고 끌 수 있습니다. - html.enabled true - xml.enabled true - csv.enabled true - - // 각 리포트 타입마다 리포트 저장 경로를 설정할 수 있습니다. - html.destination file("${buildDir}/jacoco/index.html") - xml.destination file("${buildDir}/jacoco/index.xml") - csv.destination file("${buildDir}/jacoco/index.csv") + html.required.set(true) + xml.required.set(true) + csv.required.set(true) + + // 각 리포트 타입마다 리포트 저장 경로를 설정할 수 있습니다. + html.outputLocation.set(layout.buildDirectory.dir("jacoco/html")) + xml.outputLocation.set(layout.buildDirectory.file("jacoco/index.xml")) + csv.outputLocation.set(layout.buildDirectory.file("jacoco/index.csv")) } afterEvaluate { classDirectories.setFrom( files(classDirectories.files.collect { fileTree( - dir: "$buildDir/classes/java/main", + dir: layout.buildDirectory.dir("classes/java/main").get().asFile, excludes: [ '**/*Application*', '**/*Exception*', @@ -129,7 +133,7 @@ jacocoTestReport { jacocoTestCoverageVerification { violationRules { rule { - enabled = true + enabled = false element = 'CLASS' limit { diff --git a/gradle.properties b/gradle.properties index c2b4086..de9be29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,17 +3,17 @@ applicationVersion=2.0.1-SNAPSHOT ### Project configs ### projectGroup=com.hibitbackendimproved -javaVersion=11 +javaVersion=21 ### Spring dependency versions ### -springBootVersion=2.7.1 -springDependencyManagementVersion=1.0.15.RELEASE +springBootVersion=3.3.2 +springDependencyManagementVersion=1.1.6 ### Plugin versions ### -jacocoVersion=0.8.7 -asciidoctorConvertVersion=3.3.2 +jacocoVersion=0.8.12 +asciidoctorConvertVersion=4.0.2 ### Dependency versions ### -mysqlConnectorVersion=8.0.33 -restAssuredVersion=4.4.0 +mysqlConnectorVersion=8.3.0 +restAssuredVersion=5.5.0 jjwtVersion=0.11.5 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cf5f055..a441313 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/hibitbackendimproved/auth/application/AuthService.java b/src/main/java/com/hibitbackendimproved/auth/application/AuthService.java index b78886c..343eaa4 100644 --- a/src/main/java/com/hibitbackendimproved/auth/application/AuthService.java +++ b/src/main/java/com/hibitbackendimproved/auth/application/AuthService.java @@ -8,29 +8,71 @@ import com.hibitbackendimproved.auth.dto.response.AccessAndRefreshTokenResponse; import com.hibitbackendimproved.auth.dto.response.AccessTokenResponse; import com.hibitbackendimproved.auth.event.MemberSavedEvent; +import com.hibitbackendimproved.auth.exception.ServerErrorOAuthException; import com.hibitbackendimproved.member.domain.Member; import com.hibitbackendimproved.member.domain.MemberRepository; +import com.hibitbackendimproved.member.domain.SocialType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + @Transactional(readOnly = true) @Service public class AuthService { - private final MemberRepository memberRepository; private final OAuthTokenRepository oAuthTokenRepository; private final TokenCreator tokenCreator; private final ApplicationEventPublisher eventPublisher; - + private final Map oauthUriProviders; + private final Map oauthClients; + private final Logger log = LoggerFactory.getLogger(AuthService.class); public AuthService(final MemberRepository memberRepository, final OAuthTokenRepository oAuthTokenRepository, - final TokenCreator tokenCreator, final ApplicationEventPublisher eventPublisher) { + final TokenCreator tokenCreator, final ApplicationEventPublisher eventPublisher, + final List oauthUris, final List oauthClients) { this.memberRepository = memberRepository; this.oAuthTokenRepository = oAuthTokenRepository; this.tokenCreator = tokenCreator; this.eventPublisher = eventPublisher; + this.oauthUriProviders = oauthUris.stream() + .collect(Collectors.toMap(OAuthUri::getProviderName, Function.identity())); + this.oauthClients = oauthClients.stream() + .collect(Collectors.toMap(OAuthClient::getProviderName, Function.identity())); + } + + public String generateOAuthUri(final String oauthProvider, final String redirectUri) { + final String oauthProviderName = oauthProvider.trim(); + log.info("oauth provider name: {}", oauthProviderName); + + if (!oauthUriProviders.containsKey(oauthProviderName)) { + throw new ServerErrorOAuthException("제공된 OAuth Provider가 아닙니다."); + } + return oauthUriProviders.get(oauthProviderName).generate(redirectUri); + } + + public OAuthMember handleOAuth(final String oauthProvider, final String code, final String redirectUri) { + if (!oauthUriProviders.containsKey(oauthProvider)) { + throw new ServerErrorOAuthException("제공된 OAuth Provider가 아닙니다."); + } + OAuthClient client = oauthClients.get(oauthProvider); + OAuthMember oAuthMember = client.getOAuthMember(code, redirectUri); + SocialType socialType = determineSocialType(oauthProvider); + return new OAuthMember(oAuthMember.getEmail(), oAuthMember.getNickname(), socialType, oAuthMember.getRefreshToken()); + } + + private SocialType determineSocialType(final String oauthProvider) { + return switch (oauthProvider.toLowerCase()) { + case "kakao" -> SocialType.KAKAO; + default -> throw new ServerErrorOAuthException(oauthProvider + "는 소셜 로그인에 제공하지 않습니다."); + }; } @Transactional @@ -41,8 +83,15 @@ public AccessAndRefreshTokenResponse generateAccessAndRefreshToken(final OAuthMe oAuthToken.change(oAuthMember.getRefreshToken()); AuthToken authToken = tokenCreator.createAuthToken(foundMember.getId()); + return new AccessAndRefreshTokenResponse(authToken.getAccessToken(), authToken.getRefreshToken()); + } - return new AccessAndRefreshTokenResponse(authToken.getAccessToken(), authToken.getRefreshToken(), authToken.getIsProfileRegistered()); + private Member findMember(final OAuthMember oAuthMember) { + String email = oAuthMember.getEmail(); + if (memberRepository.existsByEmail(email)) { + return memberRepository.getByEmailOrThrow(email); + } + return saveMember(oAuthMember); } private OAuthToken getOAuthToken(final OAuthMember oAuthMember, final Member member) { @@ -53,14 +102,6 @@ private OAuthToken getOAuthToken(final OAuthMember oAuthMember, final Member mem return oAuthTokenRepository.save(new OAuthToken(member, oAuthMember.getRefreshToken())); } - private Member findMember(final OAuthMember oAuthMember) { - String email = oAuthMember.getEmail(); - if (memberRepository.existsByEmail(email)) { - return memberRepository.getByEmail(email); - } - return saveMember(oAuthMember); - } - private Member saveMember(final OAuthMember oAuthMember) { Member savedMember = memberRepository.save(oAuthMember.toMember()); eventPublisher.publishEvent(new MemberSavedEvent(savedMember.getId())); diff --git a/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenCreator.java b/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenCreator.java index 9755bd7..73add13 100644 --- a/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenCreator.java +++ b/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenCreator.java @@ -2,35 +2,28 @@ import com.hibitbackendimproved.auth.domain.AuthToken; import com.hibitbackendimproved.auth.domain.TokenRepository; -import com.hibitbackendimproved.profile.domain.Profile; -import com.hibitbackendimproved.profile.domain.ProfileRepository; import org.springframework.stereotype.Component; -import java.util.Optional; - @Component public class AuthTokenCreator implements TokenCreator { private final TokenProvider tokenProvider; private final TokenRepository tokenRepository; private final AuthTokenResponseHandler authTokenResponseHandler; - private final ProfileRepository profileRepository; public AuthTokenCreator(final TokenProvider tokenProvider, final TokenRepository tokenRepository - , final AuthTokenResponseHandler authTokenResponseHandler, ProfileRepository profileRepository) { + , final AuthTokenResponseHandler authTokenResponseHandler) { this.tokenProvider = tokenProvider; this.tokenRepository = tokenRepository; this.authTokenResponseHandler = authTokenResponseHandler; - this.profileRepository = profileRepository; } public AuthToken createAuthToken(final Long memberId) { String accessToken = tokenProvider.createAccessToken(String.valueOf(memberId)); String refreshToken = createRefreshToken(memberId); - int isProfileRegistered = isProfileRegistered(memberId); // 클라이언트로 리프레시 토큰 값을 전달하는 부분 authTokenResponseHandler.setRefreshTokenCookie(refreshToken); - return new AuthToken(accessToken, refreshToken, isProfileRegistered); + return new AuthToken(accessToken, refreshToken); } private String createRefreshToken(final Long memberId) { @@ -41,25 +34,17 @@ private String createRefreshToken(final Long memberId) { return tokenRepository.save(memberId, refreshToken); } - private int isProfileRegistered(final Long memberId) { - // 데이터베이스에서 memberId를 이용하여 회원의 프로필 정보를 조회 - // 조회한 프로필 정보가 존재하면 true를 반환, 없으면 false를 반환 - Optional profile = profileRepository.findByMemberId(memberId); - return profile.isPresent() ? 1 : 0; - } - public AuthToken renewAuthToken(final String refreshToken) { tokenProvider.validateToken(refreshToken); Long memberId = Long.valueOf(tokenProvider.getPayload(refreshToken)); String accessTokenForRenew = tokenProvider.createAccessToken(String.valueOf(memberId)); String refreshTokenForRenew = tokenRepository.getToken(memberId); - int isProfileRegistered = isProfileRegistered(memberId); // 클라이언트로 리프레시 토큰 값을 전달하는 부분 authTokenResponseHandler.setRefreshTokenCookie(refreshTokenForRenew); - AuthToken renewalAuthAccessToken = new AuthToken(accessTokenForRenew, refreshTokenForRenew, isProfileRegistered); + AuthToken renewalAuthAccessToken = new AuthToken(accessTokenForRenew, refreshTokenForRenew); renewalAuthAccessToken.validateHasSameRefreshToken(refreshToken); return renewalAuthAccessToken; } diff --git a/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenResponseHandler.java b/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenResponseHandler.java index e4df141..c52542f 100644 --- a/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenResponseHandler.java +++ b/src/main/java/com/hibitbackendimproved/auth/application/AuthTokenResponseHandler.java @@ -1,10 +1,10 @@ package com.hibitbackendimproved.auth.application; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Component; -import javax.servlet.http.HttpServletResponse; @Component public class AuthTokenResponseHandler { diff --git a/src/main/java/com/hibitbackendimproved/auth/application/JwtTokenProvider.java b/src/main/java/com/hibitbackendimproved/auth/application/JwtTokenProvider.java index eac155d..24de4c0 100644 --- a/src/main/java/com/hibitbackendimproved/auth/application/JwtTokenProvider.java +++ b/src/main/java/com/hibitbackendimproved/auth/application/JwtTokenProvider.java @@ -1,7 +1,11 @@ package com.hibitbackendimproved.auth.application; import com.hibitbackendimproved.auth.exception.InvalidTokenException; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/hibitbackendimproved/auth/application/OAuthClient.java b/src/main/java/com/hibitbackendimproved/auth/application/OAuthClient.java index 5808cc7..2c9ae0f 100644 --- a/src/main/java/com/hibitbackendimproved/auth/application/OAuthClient.java +++ b/src/main/java/com/hibitbackendimproved/auth/application/OAuthClient.java @@ -2,10 +2,9 @@ import com.hibitbackendimproved.auth.dto.OAuthMember; -import com.hibitbackendimproved.auth.dto.response.OAuthAccessTokenResponse; public interface OAuthClient { OAuthMember getOAuthMember(final String code, final String redirectUri); - OAuthAccessTokenResponse getAccessToken(final String refreshToken); + String getProviderName(); } diff --git a/src/main/java/com/hibitbackendimproved/auth/application/OAuthUri.java b/src/main/java/com/hibitbackendimproved/auth/application/OAuthUri.java index 2aed7c3..a3cbb13 100644 --- a/src/main/java/com/hibitbackendimproved/auth/application/OAuthUri.java +++ b/src/main/java/com/hibitbackendimproved/auth/application/OAuthUri.java @@ -1,8 +1,8 @@ package com.hibitbackendimproved.auth.application; - -@FunctionalInterface public interface OAuthUri { String generate(final String redirectUri); + + String getProviderName(); } diff --git a/src/main/java/com/hibitbackendimproved/auth/domain/AuthToken.java b/src/main/java/com/hibitbackendimproved/auth/domain/AuthToken.java index 636e898..3a34df6 100644 --- a/src/main/java/com/hibitbackendimproved/auth/domain/AuthToken.java +++ b/src/main/java/com/hibitbackendimproved/auth/domain/AuthToken.java @@ -1,31 +1,17 @@ package com.hibitbackendimproved.auth.domain; import com.hibitbackendimproved.auth.exception.NotFoundTokenException; +import lombok.Getter; +@Getter public class AuthToken { - private String accessToken; - private String refreshToken; + private final String accessToken; + private final String refreshToken; - private int isProfileRegistered; - - public AuthToken(final String accessToken, final String refreshToken, final int isProfileRegistered) { + public AuthToken(final String accessToken, final String refreshToken) { this.accessToken = accessToken; this.refreshToken = refreshToken; - this.isProfileRegistered = isProfileRegistered; - } - - - public String getAccessToken() { - return accessToken; - } - - public String getRefreshToken() { - return refreshToken; - } - - public int getIsProfileRegistered() { - return isProfileRegistered; } public void validateHasSameRefreshToken(final String otherRefreshToken) { diff --git a/src/main/java/com/hibitbackendimproved/auth/domain/InMemoryAuthTokenRepository.java b/src/main/java/com/hibitbackendimproved/auth/domain/InMemoryAuthTokenRepository.java index 94897a6..9d51c47 100644 --- a/src/main/java/com/hibitbackendimproved/auth/domain/InMemoryAuthTokenRepository.java +++ b/src/main/java/com/hibitbackendimproved/auth/domain/InMemoryAuthTokenRepository.java @@ -23,11 +23,6 @@ public void deleteAll() { TOKEN_REPOSITORY.clear(); } - @Override - public void deleteByMemberId(final Long memberId) { - TOKEN_REPOSITORY.remove(memberId); - } - @Override public boolean exist(final Long memberId) { return TOKEN_REPOSITORY.containsKey(memberId); diff --git a/src/main/java/com/hibitbackendimproved/auth/domain/OAuthToken.java b/src/main/java/com/hibitbackendimproved/auth/domain/OAuthToken.java index 5b4ceaf..7d966e4 100644 --- a/src/main/java/com/hibitbackendimproved/auth/domain/OAuthToken.java +++ b/src/main/java/com/hibitbackendimproved/auth/domain/OAuthToken.java @@ -2,8 +2,16 @@ import com.hibitbackendimproved.common.BaseEntity; import com.hibitbackendimproved.member.domain.Member; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; -import javax.persistence.*; import java.util.Objects; @Table(name = "oauth_tokens") @@ -17,6 +25,7 @@ public class OAuthToken extends BaseEntity { @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "members_id", nullable = false) private Member member; + @Column(name = "refresh_token") private String refreshToken; diff --git a/src/main/java/com/hibitbackendimproved/auth/domain/OAuthTokenRepository.java b/src/main/java/com/hibitbackendimproved/auth/domain/OAuthTokenRepository.java index 787c74a..e061f85 100644 --- a/src/main/java/com/hibitbackendimproved/auth/domain/OAuthTokenRepository.java +++ b/src/main/java/com/hibitbackendimproved/auth/domain/OAuthTokenRepository.java @@ -13,11 +13,12 @@ public interface OAuthTokenRepository extends JpaRepository { @Query("SELECT o " + "FROM OAuthToken o " + "WHERE o.member.id = :memberId") - Optional findByMemberId(@Param("memberId") Long memberId); + Optional findByMemberId(@Param("memberId") Long memberId); default OAuthToken getByMemberId(final Long memberId) { return findByMemberId(memberId) .orElseThrow(NotFoundOAuthTokenException::new); } + void deleteAllByMemberId(final Long memberId); } diff --git a/src/main/java/com/hibitbackendimproved/auth/domain/TokenRepository.java b/src/main/java/com/hibitbackendimproved/auth/domain/TokenRepository.java index 2869d49..722b75f 100644 --- a/src/main/java/com/hibitbackendimproved/auth/domain/TokenRepository.java +++ b/src/main/java/com/hibitbackendimproved/auth/domain/TokenRepository.java @@ -6,8 +6,6 @@ public interface TokenRepository { void deleteAll(); - void deleteByMemberId(final Long memberId); - boolean exist(final Long memberId); String getToken(final Long memberId); diff --git a/src/main/java/com/hibitbackendimproved/auth/dto/OAuthMember.java b/src/main/java/com/hibitbackendimproved/auth/dto/OAuthMember.java index 906e4f8..07d6075 100644 --- a/src/main/java/com/hibitbackendimproved/auth/dto/OAuthMember.java +++ b/src/main/java/com/hibitbackendimproved/auth/dto/OAuthMember.java @@ -8,14 +8,21 @@ public class OAuthMember { private final String email; - private final String displayName; - + private final String nickname; + private SocialType socialType; private final String refreshToken; + private final boolean deleted = false; + public OAuthMember(final String email, final String nickname, final String refreshToken) { + this.email = email; + this.nickname = nickname; + this.refreshToken = refreshToken; + } - public OAuthMember(String email, String displayName, String refreshToken) { + public OAuthMember(final String email, final String nickname, final SocialType socialType, final String refreshToken) { this.email = email; - this.displayName = displayName; + this.nickname = nickname; + this.socialType = socialType; this.refreshToken = refreshToken; } @@ -23,8 +30,8 @@ public String getEmail() { return email; } - public String getDisplayName() { - return displayName; + public String getNickname() { + return nickname; } public String getRefreshToken() { @@ -32,7 +39,7 @@ public String getRefreshToken() { } public Member toMember() { - return new Member(email, displayName, SocialType.GOOGLE); + return new Member(email, nickname, socialType); } } diff --git a/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRenewalRequest.java b/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRenewalRequest.java index ff635c4..f9b6b21 100644 --- a/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRenewalRequest.java +++ b/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRenewalRequest.java @@ -1,6 +1,7 @@ package com.hibitbackendimproved.auth.dto.request; -import javax.validation.constraints.NotNull; + +import jakarta.validation.constraints.NotNull; public class TokenRenewalRequest { diff --git a/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRequest.java b/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRequest.java index 3cfc57b..bfe38b8 100644 --- a/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRequest.java +++ b/src/main/java/com/hibitbackendimproved/auth/dto/request/TokenRequest.java @@ -1,7 +1,8 @@ package com.hibitbackendimproved.auth.dto.request; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; public class TokenRequest { diff --git a/src/main/java/com/hibitbackendimproved/auth/dto/response/AccessAndRefreshTokenResponse.java b/src/main/java/com/hibitbackendimproved/auth/dto/response/AccessAndRefreshTokenResponse.java index c14d4ef..060fa06 100644 --- a/src/main/java/com/hibitbackendimproved/auth/dto/response/AccessAndRefreshTokenResponse.java +++ b/src/main/java/com/hibitbackendimproved/auth/dto/response/AccessAndRefreshTokenResponse.java @@ -1,29 +1,17 @@ package com.hibitbackendimproved.auth.dto.response; +import lombok.Getter; + +@Getter public class AccessAndRefreshTokenResponse { private final String accessToken; private final String refreshToken; - private int isProfileRegistered; - public AccessAndRefreshTokenResponse(final String accessToken, final String refreshToken - , final int isProfileRegistered) { + public AccessAndRefreshTokenResponse(final String accessToken, final String refreshToken) { this.accessToken = accessToken; this.refreshToken = refreshToken; - this.isProfileRegistered = isProfileRegistered; - } - - public String getAccessToken() { - return accessToken; - } - - public String getRefreshToken() { - return refreshToken; - } - - public int getIsProfileRegistered() { - return isProfileRegistered; } } diff --git a/src/main/java/com/hibitbackendimproved/auth/dto/response/OAuthUriResponse.java b/src/main/java/com/hibitbackendimproved/auth/dto/response/OAuthUriResponse.java index c194d00..00b31c3 100644 --- a/src/main/java/com/hibitbackendimproved/auth/dto/response/OAuthUriResponse.java +++ b/src/main/java/com/hibitbackendimproved/auth/dto/response/OAuthUriResponse.java @@ -5,9 +5,6 @@ public class OAuthUriResponse { private String oAuthUri; - private OAuthUriResponse() { - } - public OAuthUriResponse(final String oAuthUri) { this.oAuthUri = oAuthUri; } diff --git a/src/main/java/com/hibitbackendimproved/auth/exception/AuthorizationException.java b/src/main/java/com/hibitbackendimproved/auth/exception/AuthorizationException.java index 15f8e01..132dca7 100644 --- a/src/main/java/com/hibitbackendimproved/auth/exception/AuthorizationException.java +++ b/src/main/java/com/hibitbackendimproved/auth/exception/AuthorizationException.java @@ -5,6 +5,7 @@ public class AuthorizationException extends RuntimeException { public AuthorizationException(final String message) { super(message); } + public AuthorizationException() { this("권한이 없습니다."); } diff --git a/src/main/java/com/hibitbackendimproved/auth/exception/EmptyAuthorizationHeaderException.java b/src/main/java/com/hibitbackendimproved/auth/exception/EmptyAuthorizationHeaderException.java index 7e0825f..d823041 100644 --- a/src/main/java/com/hibitbackendimproved/auth/exception/EmptyAuthorizationHeaderException.java +++ b/src/main/java/com/hibitbackendimproved/auth/exception/EmptyAuthorizationHeaderException.java @@ -1,10 +1,11 @@ package com.hibitbackendimproved.auth.exception; -public class EmptyAuthorizationHeaderException extends RuntimeException{ +public class EmptyAuthorizationHeaderException extends RuntimeException { public EmptyAuthorizationHeaderException(final String message) { super(); } + public EmptyAuthorizationHeaderException() { this("Header에 Authorization이 존재하지 않습니다."); } diff --git a/src/main/java/com/hibitbackendimproved/auth/exception/OAuthException.java b/src/main/java/com/hibitbackendimproved/auth/exception/OAuthException.java new file mode 100644 index 0000000..0d25912 --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/auth/exception/OAuthException.java @@ -0,0 +1,9 @@ +package com.hibitbackendimproved.auth.exception; + + +public class OAuthException extends RuntimeException { + + public OAuthException(final String message) { + super(message); + } +} diff --git a/src/main/java/com/hibitbackendimproved/auth/exception/ServerErrorOAuthException.java b/src/main/java/com/hibitbackendimproved/auth/exception/ServerErrorOAuthException.java new file mode 100644 index 0000000..bf93997 --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/auth/exception/ServerErrorOAuthException.java @@ -0,0 +1,12 @@ +package com.hibitbackendimproved.auth.exception; + +public class ServerErrorOAuthException extends RuntimeException { + + public ServerErrorOAuthException(final String message) { + super(message); + } + + public ServerErrorOAuthException() { + this("존재하지 않는 OAuthToken 입니다."); + } +} diff --git a/src/main/java/com/hibitbackendimproved/auth/presentation/AuthController.java b/src/main/java/com/hibitbackendimproved/auth/presentation/AuthController.java index 45ec200..42a54fd 100644 --- a/src/main/java/com/hibitbackendimproved/auth/presentation/AuthController.java +++ b/src/main/java/com/hibitbackendimproved/auth/presentation/AuthController.java @@ -1,8 +1,6 @@ package com.hibitbackendimproved.auth.presentation; import com.hibitbackendimproved.auth.application.AuthService; -import com.hibitbackendimproved.auth.application.OAuthClient; -import com.hibitbackendimproved.auth.application.OAuthUri; import com.hibitbackendimproved.auth.dto.LoginMember; import com.hibitbackendimproved.auth.dto.OAuthMember; import com.hibitbackendimproved.auth.dto.request.TokenRenewalRequest; @@ -11,56 +9,58 @@ import com.hibitbackendimproved.auth.dto.response.AccessTokenResponse; import com.hibitbackendimproved.auth.dto.response.OAuthUriResponse; import com.hibitbackendimproved.support.ApiResponse; +import jakarta.validation.Valid; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; -@RequestMapping("/api/auth") +@RequestMapping("/api") @RestController public class AuthController { - private final OAuthUri oAuthUri; - private final OAuthClient oAuthClient; private final AuthService authService; - public AuthController(final OAuthUri oAuthUri, final OAuthClient oAuthClient, final AuthService authService) { - this.oAuthUri = oAuthUri; - this.oAuthClient = oAuthClient; + public AuthController(final AuthService authService) { this.authService = authService; } - @GetMapping("/{oauthProvider}/oauth-uri") + @GetMapping("/v1/auth/{oauthProvider}/oauth-uri") public ResponseEntity> generateLink(@PathVariable final String oauthProvider, - @RequestParam final String redirectUri) { - OAuthUriResponse oAuthUriResponse = new OAuthUriResponse(oAuthUri.generate(redirectUri)); + @RequestParam final String redirectUri) { + String oauthUri = authService.generateOAuthUri(oauthProvider, redirectUri); + OAuthUriResponse oAuthUriResponse = new OAuthUriResponse(oauthUri); ApiResponse apiResponse = ApiResponse.ok(oAuthUriResponse); return new ResponseEntity<>(apiResponse, HttpStatus.OK); } - @PostMapping("/{oauthProvider}/token") + + @PostMapping("/v1/auth/{oauthProvider}/token") public ResponseEntity> generateAccessAndRefreshToken( - @PathVariable final String oauthProvider, @Valid @RequestBody final TokenRequest tokenRequest) { - OAuthMember oAuthMember = oAuthClient.getOAuthMember(tokenRequest.getCode(), tokenRequest.getRedirectUri()); + @PathVariable final String oauthProvider, + @Valid @RequestBody final TokenRequest tokenRequest) { + OAuthMember oAuthMember = authService.handleOAuth(oauthProvider, tokenRequest.getCode(), tokenRequest.getRedirectUri()); AccessAndRefreshTokenResponse accessAndRefreshTokenResponse = authService.generateAccessAndRefreshToken(oAuthMember); ApiResponse apiResponse = ApiResponse.ok(accessAndRefreshTokenResponse); return new ResponseEntity<>(apiResponse, HttpStatus.OK); } - @PostMapping("/token/access") + @PostMapping("/v1/auth/token/access") public ResponseEntity> generateAccessToken( - @CookieValue("refreshToken") String refreshToken) { + @CookieValue("refreshToken") final String refreshToken) { TokenRenewalRequest tokenRenewalRequest = new TokenRenewalRequest(refreshToken); AccessTokenResponse accessTokenResponse = authService.generateAccessToken(tokenRenewalRequest); ApiResponse apiResponse = ApiResponse.ok(accessTokenResponse); return new ResponseEntity<>(apiResponse, HttpStatus.OK); } - @GetMapping("/validate/token") - public ResponseEntity validateToken(@AuthenticationPrincipal final LoginMember loginMember) { - return ResponseEntity.ok().build(); - } - @GetMapping("/logout") + @PostMapping("/v1/auth/logout") public ResponseEntity> logout(@AuthenticationPrincipal final LoginMember loginMember) { authService.deleteToken(loginMember.getId()); ApiResponse apiResponse = ApiResponse.noContent(); diff --git a/src/main/java/com/hibitbackendimproved/auth/presentation/AuthenticationPrincipalArgumentResolver.java b/src/main/java/com/hibitbackendimproved/auth/presentation/AuthenticationPrincipalArgumentResolver.java index ce5ef15..218319f 100644 --- a/src/main/java/com/hibitbackendimproved/auth/presentation/AuthenticationPrincipalArgumentResolver.java +++ b/src/main/java/com/hibitbackendimproved/auth/presentation/AuthenticationPrincipalArgumentResolver.java @@ -2,6 +2,7 @@ import com.hibitbackendimproved.auth.application.AuthService; import com.hibitbackendimproved.auth.dto.LoginMember; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -9,7 +10,6 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import javax.servlet.http.HttpServletRequest; @Component public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver { diff --git a/src/main/java/com/hibitbackendimproved/auth/presentation/AuthorizationExtractor.java b/src/main/java/com/hibitbackendimproved/auth/presentation/AuthorizationExtractor.java index 995d4fb..12cb857 100644 --- a/src/main/java/com/hibitbackendimproved/auth/presentation/AuthorizationExtractor.java +++ b/src/main/java/com/hibitbackendimproved/auth/presentation/AuthorizationExtractor.java @@ -2,9 +2,9 @@ import com.hibitbackendimproved.auth.exception.EmptyAuthorizationHeaderException; import com.hibitbackendimproved.auth.exception.InvalidTokenException; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpHeaders; -import javax.servlet.http.HttpServletRequest; import java.util.Objects; public class AuthorizationExtractor { diff --git a/src/main/java/com/hibitbackendimproved/common/BaseEntity.java b/src/main/java/com/hibitbackendimproved/common/BaseEntity.java index 10655b6..40b913f 100644 --- a/src/main/java/com/hibitbackendimproved/common/BaseEntity.java +++ b/src/main/java/com/hibitbackendimproved/common/BaseEntity.java @@ -1,12 +1,12 @@ package com.hibitbackendimproved.common; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; @MappedSuperclass @@ -14,24 +14,24 @@ public abstract class BaseEntity { @CreatedDate - @Column(name = "created_date_time", nullable = false, updatable = false) - private LocalDateTime createDateTime; + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createAt; @LastModifiedDate - @Column(name = "updated_date_time", nullable = false) - private LocalDateTime updateDateTime; + @Column(name = "updated_at", nullable = false) + private LocalDateTime updateAt; // 생성자 추가 protected BaseEntity() { - this.createDateTime = LocalDateTime.now(); - this.updateDateTime = LocalDateTime.now(); + this.createAt = LocalDateTime.now(); + this.updateAt = LocalDateTime.now(); } - public LocalDateTime getCreateDateTime() { - return createDateTime; + public LocalDateTime getCreateAt() { + return createAt; } - public LocalDateTime getUpdateDateTime() { - return updateDateTime; + public LocalDateTime getUpdateAt() { + return updateAt; } } diff --git a/src/main/java/com/hibitbackendimproved/config/PropertiesConfig.java b/src/main/java/com/hibitbackendimproved/config/PropertiesConfig.java index 25c945c..6766304 100644 --- a/src/main/java/com/hibitbackendimproved/config/PropertiesConfig.java +++ b/src/main/java/com/hibitbackendimproved/config/PropertiesConfig.java @@ -1,10 +1,10 @@ package com.hibitbackendimproved.config; -import com.hibitbackendimproved.config.properties.GoogleProperties; +import com.hibitbackendimproved.config.oauth.KakaoProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration -@EnableConfigurationProperties(GoogleProperties.class) +@EnableConfigurationProperties(KakaoProperties.class) public class PropertiesConfig { } diff --git a/src/main/java/com/hibitbackendimproved/config/WebConfig.java b/src/main/java/com/hibitbackendimproved/config/WebConfig.java index fd1eb52..34cdec2 100644 --- a/src/main/java/com/hibitbackendimproved/config/WebConfig.java +++ b/src/main/java/com/hibitbackendimproved/config/WebConfig.java @@ -24,8 +24,8 @@ public WebConfig(@Value("${cors.allow-origin.urls}") final List allowOri } @Override - public void addCorsMappings(CorsRegistry registry){ - String [] patterns = allowOriginUrlPatterns.stream() + public void addCorsMappings(CorsRegistry registry) { + String[] patterns = allowOriginUrlPatterns.stream() .toArray(String[]::new); registry.addMapping("/**") @@ -33,6 +33,7 @@ public void addCorsMappings(CorsRegistry registry){ .allowedOrigins(patterns) .allowCredentials(true); } + @Override public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(authenticationPrincipalArgumentResolver); diff --git a/src/main/java/com/hibitbackendimproved/config/oauth/KakaoProperties.java b/src/main/java/com/hibitbackendimproved/config/oauth/KakaoProperties.java new file mode 100644 index 0000000..e558d6c --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/config/oauth/KakaoProperties.java @@ -0,0 +1,37 @@ +package com.hibitbackendimproved.config.oauth; + +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +@Getter +@ConfigurationProperties("oauth.kakao") +public class KakaoProperties { + + private final String clientId; + private final String clientSecret; + private final String authorizationEndpoint; + private final String responseType; + private final List scopes; + private final String tokenUri; + private final String userInfoUri; + + public KakaoProperties(@Value("${oauth.kakao.client-id}") final String clientId, + @Value("${oauth.kakao.client-secret}") final String clientSecret, + @Value("${oauth.kakao.authorization-endpoint}") final String authorizationEndpoint, + @Value("${oauth.kakao.response-type}") final String responseType, + @Value("${oauth.kakao.scopes}") final List scopes, + @Value("${oauth.kakao.token-uri}") final String tokenUri, + @Value("${oauth.kakao.user-info-uri}") final String userInfoUri) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.authorizationEndpoint = authorizationEndpoint; + this.responseType = responseType; + this.scopes = scopes; + this.tokenUri = tokenUri; + this.userInfoUri = userInfoUri; + } + +} diff --git a/src/main/java/com/hibitbackendimproved/config/properties/GoogleProperties.java b/src/main/java/com/hibitbackendimproved/config/properties/GoogleProperties.java deleted file mode 100644 index 14dd786..0000000 --- a/src/main/java/com/hibitbackendimproved/config/properties/GoogleProperties.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.hibitbackendimproved.config.properties; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.ConstructorBinding; - -import java.util.List; - -@ConfigurationProperties("oauth.google") -@ConstructorBinding -public class GoogleProperties { - private final String clientId; - private final String clientSecret; - private final String oAuthEndPoint; - private final String responseType; - private final List scopes; - private final String tokenUri; - private final String accessType; - - public GoogleProperties(@Value("${oauth.google.client-id}") final String clientId, - @Value("${oauth.google.client-secret}") final String clientSecret, - @Value("${oauth.google.oauth-end-point}") final String oAuthEndPoint, - @Value("${oauth.google.response-type}") final String responseType, - @Value("${oauth.google.scopes}") final List scopes, - @Value("${oauth.google.token-uri}") final String tokenUri, - @Value("${oauth.google.access-type}") final String accessType) { - this.clientId = clientId; - this.clientSecret = clientSecret; - this.oAuthEndPoint = oAuthEndPoint; - this.responseType = responseType; - this.scopes = scopes; - this.tokenUri = tokenUri; - this.accessType = accessType; - } - - public String getClientId() { - return clientId; - } - - public String getClientSecret() { - return clientSecret; - } - - public String getOAuthEndPoint() { - return oAuthEndPoint; - } - - public String getResponseType() { - return responseType; - } - - public List getScopes() { - return scopes; - } - - public String getTokenUri() { - return tokenUri; - } - - public String getAccessType() { - return accessType; - } -} diff --git a/src/main/java/com/hibitbackendimproved/config/replication/DataSourceConfiguration.java b/src/main/java/com/hibitbackendimproved/config/replication/DataSourceConfiguration.java index 8b4d1c6..4c39e32 100644 --- a/src/main/java/com/hibitbackendimproved/config/replication/DataSourceConfiguration.java +++ b/src/main/java/com/hibitbackendimproved/config/replication/DataSourceConfiguration.java @@ -12,8 +12,12 @@ import javax.sql.DataSource; import java.util.Map; -import static com.hibitbackendimproved.config.replication.DataSourceKey.KeyName.*; -import static com.hibitbackendimproved.config.replication.DataSourceKey.*; +import static com.hibitbackendimproved.config.replication.DataSourceKey.KeyName.REPLICA_1_NAME; +import static com.hibitbackendimproved.config.replication.DataSourceKey.KeyName.REPLICA_2_NAME; +import static com.hibitbackendimproved.config.replication.DataSourceKey.KeyName.SOURCE_NAME; +import static com.hibitbackendimproved.config.replication.DataSourceKey.REPLICA_1; +import static com.hibitbackendimproved.config.replication.DataSourceKey.REPLICA_2; +import static com.hibitbackendimproved.config.replication.DataSourceKey.SOURCE; @Configuration @Profile("prod") diff --git a/src/main/java/com/hibitbackendimproved/config/replication/RandomReplicaKeys.java b/src/main/java/com/hibitbackendimproved/config/replication/RandomReplicaKeys.java index 827c737..ae66d86 100644 --- a/src/main/java/com/hibitbackendimproved/config/replication/RandomReplicaKeys.java +++ b/src/main/java/com/hibitbackendimproved/config/replication/RandomReplicaKeys.java @@ -14,6 +14,7 @@ public RandomReplicaKeys() { this.dataSourceKeys = List.copyOf(DataSourceKey.getReplicas()); this.size = dataSourceKeys.size(); } + public DataSourceKey next() { int currentDataSourceIndex = random.nextInt(size); return dataSourceKeys.get(currentDataSourceIndex); diff --git a/src/main/java/com/hibitbackendimproved/config/replication/RoutingDataSource.java b/src/main/java/com/hibitbackendimproved/config/replication/RoutingDataSource.java index 368abd7..d745519 100644 --- a/src/main/java/com/hibitbackendimproved/config/replication/RoutingDataSource.java +++ b/src/main/java/com/hibitbackendimproved/config/replication/RoutingDataSource.java @@ -6,13 +6,14 @@ public class RoutingDataSource extends AbstractRoutingDataSource { private final RandomReplicaKeys randomReplicaKeys = new RandomReplicaKeys(); + @Override protected Object determineCurrentLookupKey() { boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); System.out.println("Transaction의 Read Only가 " + isReadOnly + " 입니다."); - if(isReadOnly) { + if (isReadOnly) { System.out.println("Replica 서버로 요청합니다."); return randomReplicaKeys.next(); } diff --git a/src/main/java/com/hibitbackendimproved/exceptionhandler/GlobalExceptionHandler.java b/src/main/java/com/hibitbackendimproved/exceptionhandler/GlobalExceptionHandler.java index 81c8a66..6a1df06 100644 --- a/src/main/java/com/hibitbackendimproved/exceptionhandler/GlobalExceptionHandler.java +++ b/src/main/java/com/hibitbackendimproved/exceptionhandler/GlobalExceptionHandler.java @@ -4,13 +4,26 @@ import com.hibitbackendimproved.auth.exception.InvalidTokenException; import com.hibitbackendimproved.auth.exception.NotFoundOAuthTokenException; import com.hibitbackendimproved.auth.exception.NotFoundTokenException; -import com.hibitbackendimproved.support.error.dto.ErrorReportRequest; -import com.hibitbackendimproved.support.error.dto.ErrorResponse; -import com.hibitbackendimproved.infrastructure.oauth.exception.OAuthException; +import com.hibitbackendimproved.auth.exception.OAuthException; import com.hibitbackendimproved.member.exception.InvalidMemberException; import com.hibitbackendimproved.member.exception.NotFoundMemberException; -import com.hibitbackendimproved.post.exception.*; -import com.hibitbackendimproved.profile.exception.*; +import com.hibitbackendimproved.post.exception.InvalidContentException; +import com.hibitbackendimproved.post.exception.InvalidExhibitionException; +import com.hibitbackendimproved.post.exception.InvalidTitleException; +import com.hibitbackendimproved.post.exception.NotFoundImageFileException; +import com.hibitbackendimproved.post.exception.NotFoundPostStatusException; +import com.hibitbackendimproved.post.exception.NotFoundTogetherActivityException; +import com.hibitbackendimproved.profile.exception.InvalidIntroduceException; +import com.hibitbackendimproved.profile.exception.InvalidNicknameException; +import com.hibitbackendimproved.profile.exception.InvalidProfileAlreadyException; +import com.hibitbackendimproved.profile.exception.NicknameAlreadyTakenException; +import com.hibitbackendimproved.profile.exception.NotFoundAddressCityException; +import com.hibitbackendimproved.profile.exception.NotFoundAddressDistrictException; +import com.hibitbackendimproved.profile.exception.NotFoundPersonalityException; +import com.hibitbackendimproved.profile.exception.NotFoundProfileException; +import com.hibitbackendimproved.support.error.dto.ErrorReportRequest; +import com.hibitbackendimproved.support.error.dto.ErrorResponse; +import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -22,7 +35,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import javax.servlet.http.HttpServletRequest; @RestControllerAdvice public class GlobalExceptionHandler { diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/client/GoogleOAuthClient.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/client/GoogleOAuthClient.java deleted file mode 100644 index 7cd81ed..0000000 --- a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/client/GoogleOAuthClient.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.hibitbackendimproved.infrastructure.oauth.client; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.hibitbackendimproved.auth.application.OAuthClient; -import com.hibitbackendimproved.auth.dto.OAuthMember; -import com.hibitbackendimproved.auth.dto.response.OAuthAccessTokenResponse; -import com.hibitbackendimproved.config.properties.GoogleProperties; -import com.hibitbackendimproved.infrastructure.oauth.dto.GoogleTokenResponse; -import com.hibitbackendimproved.infrastructure.oauth.dto.GoogleUserInfo; -import com.hibitbackendimproved.infrastructure.oauth.exception.OAuthException; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -@Component -public class GoogleOAuthClient implements OAuthClient { - - private static final String JWT_DELIMITER = "\\."; - - private final GoogleProperties properties; - private final RestTemplate restTemplate; - private final ObjectMapper objectMapper; - - public GoogleOAuthClient(final GoogleProperties properties, - final RestTemplateBuilder restTemplateBuilder, - final ObjectMapper objectMapper) { - this.properties = properties; - this.restTemplate = restTemplateBuilder.build(); - this.objectMapper = objectMapper; - } - - @Override - public OAuthMember getOAuthMember(final String code, final String redirectUri) { - GoogleTokenResponse googleTokenResponse = requestGoogleToken(code, redirectUri); - String payload = getPayload(googleTokenResponse.getIdToken()); - GoogleUserInfo googleUserInfo = parseUserInfo(payload); - - String refreshToken = googleTokenResponse.getRefreshToken(); - return new OAuthMember(googleUserInfo.getEmail(), googleUserInfo.getName(), refreshToken); - } - - private GoogleTokenResponse requestGoogleToken(final String code, final String redirectUri) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap params = generateTokenParams(code, redirectUri); - - HttpEntity> request = new HttpEntity<>(params, headers); - return fetchGoogleToken(request).getBody(); - } - - private MultiValueMap generateTokenParams(final String code, final String redirectUri) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("client_id", properties.getClientId()); - params.add("client_secret", properties.getClientSecret()); - params.add("code", code); - params.add("grant_type", "authorization_code"); - params.add("redirect_uri", redirectUri); - return params; - } - - private ResponseEntity fetchGoogleToken( - final HttpEntity> request) { - try { - return restTemplate.postForEntity(properties.getTokenUri(), request, GoogleTokenResponse.class); - } catch (final RestClientException e) { - throw new OAuthException(e); - } - } - - private String getPayload(final String jwt) { - return jwt.split(JWT_DELIMITER)[1]; - } - - private GoogleUserInfo parseUserInfo(final String payload) { - String decodedPayload = decodeJwtPayload(payload); - try { - return objectMapper.readValue(decodedPayload, GoogleUserInfo.class); - } catch (final JsonProcessingException e) { - throw new OAuthException("id 토큰을 읽을 수 없습니다.", e); - } - } - - private String decodeJwtPayload(final String payload) { - return new String(Base64.getUrlDecoder().decode(payload), StandardCharsets.UTF_8); - } - - @Override - public OAuthAccessTokenResponse getAccessToken(final String refreshToken) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap params = generateAccessTokenParams(refreshToken); - - HttpEntity> request = new HttpEntity<>(params, headers); - return fetchGoogleAccessToken(request).getBody(); - } - - private MultiValueMap generateAccessTokenParams(final String refreshToken) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("client_id", properties.getClientId()); - params.add("client_secret", properties.getClientSecret()); - params.add("refresh_token", refreshToken); - params.add("grant_type", "refresh_token"); - return params; - } - - private ResponseEntity fetchGoogleAccessToken( - final HttpEntity> request) { - try { - return restTemplate.postForEntity(properties.getTokenUri(), request, OAuthAccessTokenResponse.class); - } catch (final RestClientException e) { - throw new OAuthException(e); - } - } -} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/client/KakaoOAuthClient.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/client/KakaoOAuthClient.java new file mode 100644 index 0000000..3eb0a64 --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/client/KakaoOAuthClient.java @@ -0,0 +1,96 @@ +package com.hibitbackendimproved.infrastructure.oauth.client; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hibitbackendimproved.auth.application.OAuthClient; +import com.hibitbackendimproved.auth.dto.OAuthMember; +import com.hibitbackendimproved.auth.exception.ServerErrorOAuthException; +import com.hibitbackendimproved.config.oauth.KakaoProperties; +import com.hibitbackendimproved.infrastructure.oauth.dto.KakaoTokenResponse; +import com.hibitbackendimproved.infrastructure.oauth.dto.KakaoUserInfo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Component +public class KakaoOAuthClient implements OAuthClient { + + private static final String KAKAO = "kakao"; + private final KakaoProperties properties; + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + + public KakaoOAuthClient(final KakaoProperties kakaoProperties, + final RestTemplateBuilder restTemplateBuilder, + final ObjectMapper objectMapper) { + this.properties = kakaoProperties; + this.restTemplate = restTemplateBuilder.build(); + this.objectMapper = objectMapper; + } + + @Override + public OAuthMember getOAuthMember(final String code, final String redirectUri) { + KakaoTokenResponse kakaoTokenResponse = requestKakaoToken(code, redirectUri); + KakaoUserInfo kakaoUserInfo = requestUserInfo(kakaoTokenResponse.getAccessToken()); + + String refreshToken = kakaoTokenResponse.getAccessToken(); + return new OAuthMember(kakaoUserInfo.getNickname(), kakaoUserInfo.getProfileImage(), refreshToken); + } + + private KakaoTokenResponse requestKakaoToken(final String code, final String redirectUri) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + MultiValueMap params = generateTokenParams(code, redirectUri); + + HttpEntity> request = new HttpEntity<>(params, headers); + return fetchKakaoToken(request).getBody(); + + } + + private MultiValueMap generateTokenParams(final String code, final String redirectUri) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", "authorization_code"); + params.add("client_id", properties.getClientId()); + params.add("redirect_uri", redirectUri); + params.add("code", code); + params.add("client_secret", properties.getClientSecret()); + return params; + } + + private ResponseEntity fetchKakaoToken(final HttpEntity> request) { + try { + return restTemplate.postForEntity(properties.getTokenUri(), request, KakaoTokenResponse.class); + } catch (final RestClientException e) { + throw new ServerErrorOAuthException(); + } + } + + private KakaoUserInfo requestUserInfo(final String accessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", "Bearer " + accessToken); + headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + HttpEntity request = new HttpEntity<>(headers); + + try { + ResponseEntity response = restTemplate.exchange(properties.getUserInfoUri(), HttpMethod.GET, request, String.class); + // ObjectMapper를 사용하여 JSON 응답을 KakaoUserInfo 객체로 변환 + return objectMapper.readValue(response.getBody(), KakaoUserInfo.class); + } catch (RestClientException | JsonProcessingException e) { + throw new ServerErrorOAuthException("Failed to fetch user info"); + } + } + + @Override + public String getProviderName() { + return KAKAO; + } +} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/GoogleTokenResponse.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/GoogleTokenResponse.java deleted file mode 100644 index 0517715..0000000 --- a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/GoogleTokenResponse.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.hibitbackendimproved.infrastructure.oauth.dto; - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; - -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class GoogleTokenResponse { - private String accessToken; - private String refreshToken; - private String idToken; - private String expiresIn; - private String tokenType; - private String scope; - - private GoogleTokenResponse() { - } - - public GoogleTokenResponse(final String accessToken, final String refreshToken, final String idToken, - final String expiresIn, final String scope, final String tokenType) { - this.accessToken = accessToken; - this.refreshToken = refreshToken; - this.idToken = idToken; - this.expiresIn = expiresIn; - this.scope = scope; - this.tokenType = tokenType; - } - - public String getAccessToken() { - return accessToken; - } - - public String getRefreshToken() { - return refreshToken; - } - - public String getIdToken() { - return idToken; - } - - public String getExpiresIn() { - return expiresIn; - } - - public String getScope() { - return scope; - } - - public String getTokenType() { - return tokenType; - } -} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/GoogleUserInfo.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/GoogleUserInfo.java deleted file mode 100644 index bf97495..0000000 --- a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/GoogleUserInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.hibitbackendimproved.infrastructure.oauth.dto; - -public class GoogleUserInfo { - - private String email; - private String name; - - - private GoogleUserInfo() { - } - - public GoogleUserInfo(String email, String name) { - this.email = email; - this.name = name; - } - - public String getEmail() { - return email; - } - - public String getName() { - return name; - } - -} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/KakaoTokenResponse.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/KakaoTokenResponse.java new file mode 100644 index 0000000..1cd32a0 --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/KakaoTokenResponse.java @@ -0,0 +1,34 @@ +package com.hibitbackendimproved.infrastructure.oauth.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; + +@Getter +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoTokenResponse { + + private String accessToken; + private String refreshToken; + private String idToken; + private String expiresIn; + private String tokenType; + private String scope; + + private KakaoTokenResponse() { + } + + public KakaoTokenResponse(final String accessToken, + final String refreshToken, + final String idToken, + final String expiresIn, + final String tokenType, + final String scope) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.idToken = idToken; + this.expiresIn = expiresIn; + this.tokenType = tokenType; + this.scope = scope; + } +} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/KakaoUserInfo.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/KakaoUserInfo.java new file mode 100644 index 0000000..3692528 --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/dto/KakaoUserInfo.java @@ -0,0 +1,18 @@ +package com.hibitbackendimproved.infrastructure.oauth.dto; + +import lombok.Getter; + +@Getter +public class KakaoUserInfo { + + private String nickname; + private String profileImage; + + private KakaoUserInfo() { + } + + public KakaoUserInfo(final String nickname, final String profileImage) { + this.nickname = nickname; + this.profileImage = profileImage; + } +} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/exception/OAuthException.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/exception/OAuthException.java deleted file mode 100644 index 24d0b04..0000000 --- a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/exception/OAuthException.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.hibitbackendimproved.infrastructure.oauth.exception; - - -public class OAuthException extends RuntimeException{ - public OAuthException() { - super("Oauth 서버와의 통신 과정에서 문제가 발생했습니다."); - } - - public OAuthException(final Exception e) { - this("Oauth 서버와의 통신 과정에서 문제가 발생했습니다.", e); - } - - public OAuthException(final String message, final Exception e) { - super(message, e); - } -} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/uri/GoogleOAuthUri.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/uri/GoogleOAuthUri.java deleted file mode 100644 index e17b66b..0000000 --- a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/uri/GoogleOAuthUri.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.hibitbackendimproved.infrastructure.oauth.uri; - -import com.hibitbackendimproved.auth.application.OAuthUri; -import com.hibitbackendimproved.config.properties.GoogleProperties; -import org.springframework.stereotype.Component; - -@Component -public class GoogleOAuthUri implements OAuthUri { - - private final GoogleProperties properties; - - public GoogleOAuthUri(final GoogleProperties properties) { - this.properties = properties; - } - - @Override - public String generate(final String redirectUri) { - return properties.getOAuthEndPoint() + "?" - + "client_id=" + properties.getClientId() + "&" - + "redirect_uri=" + redirectUri + "&" - + "response_type=code&" - + "scope=" + String.join(" ", properties.getScopes()) + "&" - + "access_type=" + properties.getAccessType(); - } -} diff --git a/src/main/java/com/hibitbackendimproved/infrastructure/oauth/uri/KakaoOAuthUri.java b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/uri/KakaoOAuthUri.java new file mode 100644 index 0000000..1672d68 --- /dev/null +++ b/src/main/java/com/hibitbackendimproved/infrastructure/oauth/uri/KakaoOAuthUri.java @@ -0,0 +1,31 @@ +package com.hibitbackendimproved.infrastructure.oauth.uri; + +import com.hibitbackendimproved.auth.application.OAuthUri; +import com.hibitbackendimproved.config.oauth.KakaoProperties; +import org.springframework.stereotype.Component; + +@Component +public class KakaoOAuthUri implements OAuthUri { + + private static final String KAKAO = "kakao"; + private final KakaoProperties properties; + + public KakaoOAuthUri(final KakaoProperties properties) { + this.properties = properties; + } + + @Override + public String generate(final String redirectUri) { + return properties.getAuthorizationEndpoint() + "?" + + "response_type=" + properties.getResponseType() + "&" + + "client_id=" + properties.getClientId() + "&" + + "redirect_uri=" + redirectUri + "&" + + "scope=" + String.join(" ", properties.getScopes()); + + } + + @Override + public String getProviderName() { + return KAKAO; + } +} diff --git a/src/main/java/com/hibitbackendimproved/member/application/MemberService.java b/src/main/java/com/hibitbackendimproved/member/application/MemberService.java index 9d5bdb9..5a369cd 100644 --- a/src/main/java/com/hibitbackendimproved/member/application/MemberService.java +++ b/src/main/java/com/hibitbackendimproved/member/application/MemberService.java @@ -16,6 +16,6 @@ public MemberService(final MemberRepository memberRepository) { } public MemberResponse findById(final Long id) { - return new MemberResponse(memberRepository.getById(id)); + return new MemberResponse(memberRepository.getByIdOrThrow(id)); } } diff --git a/src/main/java/com/hibitbackendimproved/member/domain/Member.java b/src/main/java/com/hibitbackendimproved/member/domain/Member.java index 5cd2d75..564c322 100644 --- a/src/main/java/com/hibitbackendimproved/member/domain/Member.java +++ b/src/main/java/com/hibitbackendimproved/member/domain/Member.java @@ -2,18 +2,27 @@ import com.hibitbackendimproved.common.BaseEntity; import com.hibitbackendimproved.member.exception.InvalidMemberException; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.Builder; +import lombok.Getter; -import javax.persistence.*; import java.util.regex.Matcher; import java.util.regex.Pattern; - +@Getter @Table(name = "members") @Entity public class Member extends BaseEntity { private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-z0-9._-]+@[a-z]+[.]+[a-z]{2,3}$"); private static final int MAX_DISPLAY_NAME_LENGTH = 20; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -22,30 +31,24 @@ public class Member extends BaseEntity { @Column(name = "email", nullable = false) private String email; - @Column(name = "display_name", nullable = false) - private String displayName; // 구글로부터 가져오는 닉네임 + @Column(name = "nickname", nullable = false) + private String nickname; @Enumerated(value = EnumType.STRING) - @Column(name = "social_type", nullable = false) + @Column(name = "social_type", nullable = true) private SocialType socialType; - @Column(nullable = false, columnDefinition = "boolean default false") - private boolean isProfile; - - @Column(name = "main_image", nullable = true) - private String mainImage; - protected Member() { } @Builder - public Member(final String email, final String displayName, final SocialType socialType) { + public Member(final String email, final String nickname, final SocialType socialType) { super(); validateEmail(email); - validateDisplayName(displayName); + validateNickName(nickname); this.email = email; - this.displayName = displayName; + this.nickname = nickname; this.socialType = socialType; } @@ -56,49 +59,13 @@ private void validateEmail(final String email) { } } - /** - * isEmpty() 는 "" 에 대한 문자열을 확인 -> 비어있는 경우 true 반환 - * isBlank() "", " " 에 대한 문자열을 확인 -> 비어있는 경우 true 반환 - */ - private void validateDisplayName(final String displayName) { - if (displayName.isBlank() || displayName.length() > MAX_DISPLAY_NAME_LENGTH) { + private void validateNickName(final String nickname) { + if (nickname.isBlank() || nickname.length() > MAX_DISPLAY_NAME_LENGTH) { throw new InvalidMemberException(String.format("이름은 1자 이상 20자 %d이하여야 합니다.", MAX_DISPLAY_NAME_LENGTH)); } } - public Long getId() { - return id; - } - - public String getEmail() { - return email; - } - - public String getDisplayName() { - return displayName; - } - - public SocialType getSocialType() { - return socialType; - } - - public String getMainImage() { - return mainImage; - } - - public void updateDisplayName(final String nickname) { - this.displayName = nickname; - } - - public boolean getIsprofile() { - return isProfile; - } - - public void updateIsprofile() { - this.isProfile = true; - } - - public void updateMainImage(final String mainImage) { - this.mainImage = mainImage; + public void updateNickname(final String nickname) { + this.nickname = nickname; } } diff --git a/src/main/java/com/hibitbackendimproved/member/domain/MemberRepository.java b/src/main/java/com/hibitbackendimproved/member/domain/MemberRepository.java index e787cc2..d941d16 100644 --- a/src/main/java/com/hibitbackendimproved/member/domain/MemberRepository.java +++ b/src/main/java/com/hibitbackendimproved/member/domain/MemberRepository.java @@ -1,7 +1,7 @@ package com.hibitbackendimproved.member.domain; -import org.springframework.data.jpa.repository.JpaRepository; import com.hibitbackendimproved.member.exception.NotFoundMemberException; +import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; @@ -9,14 +9,14 @@ public interface MemberRepository extends JpaRepository { Optional findByEmail(final String email); - default Member getById(final Long id) { + default Member getByIdOrThrow(final Long id) { return findById(id) .orElseThrow(NotFoundMemberException::new); } boolean existsByEmail(final String email); - default Member getByEmail(final String email) { + default Member getByEmailOrThrow(final String email) { return findByEmail(email) .orElseThrow(NotFoundMemberException::new); } diff --git a/src/main/java/com/hibitbackendimproved/member/domain/SocialType.java b/src/main/java/com/hibitbackendimproved/member/domain/SocialType.java index 520aabc..3297dd4 100644 --- a/src/main/java/com/hibitbackendimproved/member/domain/SocialType.java +++ b/src/main/java/com/hibitbackendimproved/member/domain/SocialType.java @@ -6,6 +6,5 @@ @Getter @RequiredArgsConstructor public enum SocialType { - GOOGLE, KAKAO; - + KAKAO; } diff --git a/src/main/java/com/hibitbackendimproved/member/dto/MemberResponse.java b/src/main/java/com/hibitbackendimproved/member/dto/MemberResponse.java index 40f3d52..71dbf0a 100644 --- a/src/main/java/com/hibitbackendimproved/member/dto/MemberResponse.java +++ b/src/main/java/com/hibitbackendimproved/member/dto/MemberResponse.java @@ -3,46 +3,26 @@ import com.hibitbackendimproved.member.domain.Member; import com.hibitbackendimproved.member.domain.SocialType; +import lombok.Getter; +@Getter public class MemberResponse { private Long id; private String email; private String displayName; private SocialType socialType; - private boolean isProfile; private MemberResponse() { } - public MemberResponse(final Long id, final String email, final String displayName, final SocialType socialType, final boolean isProfile) { + public MemberResponse(final Long id, final String email, final String displayName, final SocialType socialType) { this.id = id; this.email = email; this.displayName = displayName; this.socialType = socialType; - this.isProfile = isProfile; } public MemberResponse(final Member member) { - this(member.getId(), member.getEmail(), member.getDisplayName(), member.getSocialType(), member.getIsprofile()); - } - - public Long getId() { - return id; - } - - public String getEmail() { - return email; - } - - public String getDisplayName() { - return displayName; - } - - public SocialType getSocialType() { - return socialType; - } - - public boolean isProfile() { - return isProfile; + this(member.getId(), member.getEmail(), member.getNickname(), member.getSocialType()); } } diff --git a/src/main/java/com/hibitbackendimproved/member/presentation/MemberController.java b/src/main/java/com/hibitbackendimproved/member/presentation/MemberController.java index 460ea2d..b654502 100644 --- a/src/main/java/com/hibitbackendimproved/member/presentation/MemberController.java +++ b/src/main/java/com/hibitbackendimproved/member/presentation/MemberController.java @@ -2,9 +2,9 @@ import com.hibitbackendimproved.auth.dto.LoginMember; import com.hibitbackendimproved.auth.presentation.AuthenticationPrincipal; -import com.hibitbackendimproved.support.ApiResponse; import com.hibitbackendimproved.member.application.MemberService; import com.hibitbackendimproved.member.dto.MemberResponse; +import com.hibitbackendimproved.support.ApiResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/hibitbackendimproved/post/application/PostService.java b/src/main/java/com/hibitbackendimproved/post/application/PostService.java index a9ed411..7fe950d 100644 --- a/src/main/java/com/hibitbackendimproved/post/application/PostService.java +++ b/src/main/java/com/hibitbackendimproved/post/application/PostService.java @@ -48,7 +48,7 @@ public PostService(final PostRepository postRepository, final MemberRepository m @Transactional public PostDetailResponse save(final LoginMember loginMember, final PostCreateRequest request) { validateMember(loginMember.getId()); - Member foundMember = memberRepository.getById(loginMember.getId()); + Member foundMember = memberRepository.getByIdOrThrow(loginMember.getId()); Post savedPost = request.toEntity(foundMember, request); postRepository.save(savedPost); return PostDetailResponse.of(savedPost, loginMember); @@ -92,7 +92,7 @@ private Post findPostObject(final Long postId) { } public PostsCountResponse countPostWithQuery(final String query) { - Pageable pageable = PageRequest.of(0, 3, DESC, "created_date_time"); + Pageable pageable = PageRequest.of(0, 3, DESC, "created_at"); SearchQuery searchQuery = new SearchQuery(query); Page posts = postRepository.findPostPagesByQuery(pageable, searchQuery.getValue()); @@ -100,7 +100,7 @@ public PostsCountResponse countPostWithQuery(final String query) { } public PostsSliceResponse searchSlickWithQuery(final String query, Pageable pageable) { - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), DESC, "created_date_time"); + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), DESC, "created_at"); SearchQuery searchQuery = new SearchQuery(query); Slice posts = postRepository.findPostSlicePageByQuery(pageable, searchQuery.getValue()); @@ -109,7 +109,7 @@ public PostsSliceResponse searchSlickWithQuery(final String query, Pageable page @Transactional public void update(final Long memberId, final Long postId, final PostUpdateServiceRequest request) { - Member member = memberRepository.getById(memberId); + Member member = memberRepository.getByIdOrThrow(memberId); Post post = findPostObject(postId); validateProductMembership(memberId, post); diff --git a/src/main/java/com/hibitbackendimproved/post/domain/Content.java b/src/main/java/com/hibitbackendimproved/post/domain/Content.java index 15d38f9..7c73cc9 100644 --- a/src/main/java/com/hibitbackendimproved/post/domain/Content.java +++ b/src/main/java/com/hibitbackendimproved/post/domain/Content.java @@ -1,11 +1,11 @@ package com.hibitbackendimproved.post.domain; import com.hibitbackendimproved.post.exception.InvalidContentException; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Lob; import lombok.Getter; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Lob; import java.util.Objects; @Getter diff --git a/src/main/java/com/hibitbackendimproved/post/domain/Exhibition.java b/src/main/java/com/hibitbackendimproved/post/domain/Exhibition.java index 5dcc6bc..b436287 100644 --- a/src/main/java/com/hibitbackendimproved/post/domain/Exhibition.java +++ b/src/main/java/com/hibitbackendimproved/post/domain/Exhibition.java @@ -1,11 +1,11 @@ package com.hibitbackendimproved.post.domain; import com.hibitbackendimproved.post.exception.InvalidExhibitionException; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Lob; import lombok.Getter; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Lob; import java.util.Objects; @Getter diff --git a/src/main/java/com/hibitbackendimproved/post/domain/Post.java b/src/main/java/com/hibitbackendimproved/post/domain/Post.java index 04c0279..b7bf508 100644 --- a/src/main/java/com/hibitbackendimproved/post/domain/Post.java +++ b/src/main/java/com/hibitbackendimproved/post/domain/Post.java @@ -2,9 +2,20 @@ import com.hibitbackendimproved.common.BaseEntity; import com.hibitbackendimproved.member.domain.Member; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import lombok.Builder; -import javax.persistence.*; import java.time.LocalDateTime; @Table(name = "posts") diff --git a/src/main/java/com/hibitbackendimproved/post/domain/PostRepository.java b/src/main/java/com/hibitbackendimproved/post/domain/PostRepository.java index 31e9d21..4c10624 100644 --- a/src/main/java/com/hibitbackendimproved/post/domain/PostRepository.java +++ b/src/main/java/com/hibitbackendimproved/post/domain/PostRepository.java @@ -23,7 +23,7 @@ public interface PostRepository extends JpaRepository { @Query(value = "SELECT * " + "FROM posts p " + - "ORDER BY p.created_date_time DESC", nativeQuery = true) + "ORDER BY p.created_at DESC", nativeQuery = true) List findAllByOrderByCreatedDateTimeDesc(); @Transactional diff --git a/src/main/java/com/hibitbackendimproved/post/domain/Title.java b/src/main/java/com/hibitbackendimproved/post/domain/Title.java index b26adc9..3526fd3 100644 --- a/src/main/java/com/hibitbackendimproved/post/domain/Title.java +++ b/src/main/java/com/hibitbackendimproved/post/domain/Title.java @@ -1,10 +1,10 @@ package com.hibitbackendimproved.post.domain; import com.hibitbackendimproved.post.exception.InvalidTitleException; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; import lombok.Getter; -import javax.persistence.Column; -import javax.persistence.Embeddable; import java.util.Objects; @Getter diff --git a/src/main/java/com/hibitbackendimproved/post/domain/ViewCountManager.java b/src/main/java/com/hibitbackendimproved/post/domain/ViewCountManager.java index 5427af9..7bca0fb 100644 --- a/src/main/java/com/hibitbackendimproved/post/domain/ViewCountManager.java +++ b/src/main/java/com/hibitbackendimproved/post/domain/ViewCountManager.java @@ -3,7 +3,12 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; @Component diff --git a/src/main/java/com/hibitbackendimproved/post/dto/request/PostCreateRequest.java b/src/main/java/com/hibitbackendimproved/post/dto/request/PostCreateRequest.java index 52d91b7..1e366c7 100644 --- a/src/main/java/com/hibitbackendimproved/post/dto/request/PostCreateRequest.java +++ b/src/main/java/com/hibitbackendimproved/post/dto/request/PostCreateRequest.java @@ -5,12 +5,12 @@ import com.hibitbackendimproved.post.domain.Post; import com.hibitbackendimproved.post.domain.PostStatus; import com.hibitbackendimproved.post.domain.TogetherActivity; +import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; import java.time.LocalDateTime; @Getter diff --git a/src/main/java/com/hibitbackendimproved/post/dto/request/PostUpdateRequest.java b/src/main/java/com/hibitbackendimproved/post/dto/request/PostUpdateRequest.java index 34c8caa..c08f460 100644 --- a/src/main/java/com/hibitbackendimproved/post/dto/request/PostUpdateRequest.java +++ b/src/main/java/com/hibitbackendimproved/post/dto/request/PostUpdateRequest.java @@ -3,12 +3,12 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.hibitbackendimproved.post.domain.PostStatus; import com.hibitbackendimproved.post.domain.TogetherActivity; +import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; import java.time.LocalDateTime; @Getter diff --git a/src/main/java/com/hibitbackendimproved/post/dto/response/PostDetailResponse.java b/src/main/java/com/hibitbackendimproved/post/dto/response/PostDetailResponse.java index 1ba5c18..c6d09e9 100644 --- a/src/main/java/com/hibitbackendimproved/post/dto/response/PostDetailResponse.java +++ b/src/main/java/com/hibitbackendimproved/post/dto/response/PostDetailResponse.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.hibitbackendimproved.auth.dto.LoginMember; -import com.hibitbackendimproved.member.domain.Member; import com.hibitbackendimproved.post.domain.Post; import com.hibitbackendimproved.post.domain.PostStatus; import lombok.AccessLevel; @@ -18,8 +17,6 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class PostDetailResponse { - private static final String HIBIT_BASIC_IMAGE = "https://hibitbucket.s3.ap-northeast-2.amazonaws.com/hibit-image.png"; - private Long id; private Long writerId; private String writerName; @@ -57,7 +54,7 @@ public static PostDetailResponse of(final Post post, final LoginMember loginMemb return PostDetailResponse.builder() .id(post.getId()) .writerId(loginMember.getId()) - .writerName(post.getMember().getDisplayName()) + .writerName(post.getMember().getNickname()) .title(post.getTitle()) .content(post.getContent()) .exhibition(post.getExhibition()) @@ -69,11 +66,4 @@ public static PostDetailResponse of(final Post post, final LoginMember loginMemb .viewCount(post.getViewCount()) .build(); } - - private static String findWriterImage(final Member member) { - if(!member.getMainImage().isEmpty()) { - return member.getMainImage(); - } - return HIBIT_BASIC_IMAGE; - } } diff --git a/src/main/java/com/hibitbackendimproved/post/dto/response/PostResponse.java b/src/main/java/com/hibitbackendimproved/post/dto/response/PostResponse.java index 779d8ed..c601d6e 100644 --- a/src/main/java/com/hibitbackendimproved/post/dto/response/PostResponse.java +++ b/src/main/java/com/hibitbackendimproved/post/dto/response/PostResponse.java @@ -44,7 +44,7 @@ public static PostResponse from(final Post post) { .exhibitionAttendanceAndTogetherActivity(AttendanceAndTogetherActivity(post.getExhibitionAttendance(), post.getTogetherActivity())) .postStatus(post.getPostStatus()) .imageName(post.getImageName()) - .createDateTime(post.getCreateDateTime()) + .createDateTime(post.getCreateAt()) .build(); } diff --git a/src/main/java/com/hibitbackendimproved/post/dto/response/PostsResponse.java b/src/main/java/com/hibitbackendimproved/post/dto/response/PostsResponse.java index e2d426c..389a998 100644 --- a/src/main/java/com/hibitbackendimproved/post/dto/response/PostsResponse.java +++ b/src/main/java/com/hibitbackendimproved/post/dto/response/PostsResponse.java @@ -16,7 +16,7 @@ public PostsResponse(final List posts) { public static PostsResponse of(final List posts) { List postResponses = posts.stream() - .map(post -> PostResponse.from(post)) + .map(post -> PostResponse.from(post)) .collect(Collectors.toList()); return new PostsResponse(postResponses); } diff --git a/src/main/java/com/hibitbackendimproved/post/dto/response/PostsSliceResponse.java b/src/main/java/com/hibitbackendimproved/post/dto/response/PostsSliceResponse.java index 38e4d0c..7744693 100644 --- a/src/main/java/com/hibitbackendimproved/post/dto/response/PostsSliceResponse.java +++ b/src/main/java/com/hibitbackendimproved/post/dto/response/PostsSliceResponse.java @@ -20,7 +20,7 @@ public PostsSliceResponse(final List posts, final boolean lastPage public static PostsSliceResponse ofPostSlice(final Slice postSlice) { List postResponses = postSlice.getContent() .stream() - .map(post -> PostResponse.from(post)) + .map(post -> PostResponse.from(post)) .collect(Collectors.toList()); return new PostsSliceResponse(postResponses, postSlice.isLast()); } diff --git a/src/main/java/com/hibitbackendimproved/post/presentation/PostController.java b/src/main/java/com/hibitbackendimproved/post/presentation/PostController.java index 3e582db..5ce8f99 100644 --- a/src/main/java/com/hibitbackendimproved/post/presentation/PostController.java +++ b/src/main/java/com/hibitbackendimproved/post/presentation/PostController.java @@ -2,7 +2,6 @@ import com.hibitbackendimproved.auth.dto.LoginMember; import com.hibitbackendimproved.auth.presentation.AuthenticationPrincipal; -import com.hibitbackendimproved.support.ApiResponse; import com.hibitbackendimproved.post.application.PostService; import com.hibitbackendimproved.post.dto.request.PostCreateRequest; import com.hibitbackendimproved.post.dto.request.PostUpdateRequest; @@ -10,6 +9,8 @@ import com.hibitbackendimproved.post.dto.response.PostsCountResponse; import com.hibitbackendimproved.post.dto.response.PostsResponse; import com.hibitbackendimproved.post.dto.response.PostsSliceResponse; +import com.hibitbackendimproved.support.ApiResponse; +import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpHeaders; @@ -17,9 +18,15 @@ import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import static org.springframework.data.domain.Sort.Direction.DESC; diff --git a/src/main/java/com/hibitbackendimproved/profile/application/ProfileService.java b/src/main/java/com/hibitbackendimproved/profile/application/ProfileService.java index b3fbf9f..8987b65 100644 --- a/src/main/java/com/hibitbackendimproved/profile/application/ProfileService.java +++ b/src/main/java/com/hibitbackendimproved/profile/application/ProfileService.java @@ -32,7 +32,7 @@ public ProfileResponse save(final Long memberId, final ProfileCreateRequest requ if (profileRepository.existsByMemberId(memberId)) { throw new InvalidProfileAlreadyException("프로필이 이미 존재합니다."); } - Member foundMember = memberRepository.getById(memberId); + Member foundMember = memberRepository.getByIdOrThrow(memberId); Profile profile = createProfile(request, foundMember); Profile savedProfile = profileRepository.save(profile); @@ -65,18 +65,16 @@ private void validateExistByNickname(final String nickname) { } private void updateMemberInfo(final Member member, final Profile profile) { - member.updateDisplayName(profile.getNickname()); - member.updateIsprofile(); - member.updateMainImage(profile.getImageName()); + member.updateNickname(profile.getNickname()); memberRepository.save(member); } @Transactional public void update(final Long memberId, final ProfileUpdateRequest request) { Profile profile = profileRepository.findByMemberId(memberId) - .orElseThrow(NotFoundProfileException::new); + .orElseThrow(NotFoundProfileException::new); validateExistByNickname(request.getNickname()); - Member foundMember = memberRepository.getById(memberId); + Member foundMember = memberRepository.getByIdOrThrow(memberId); updateProfileInfo(profile, request); updateMemberInfo(foundMember, profile); diff --git a/src/main/java/com/hibitbackendimproved/profile/domain/Profile.java b/src/main/java/com/hibitbackendimproved/profile/domain/Profile.java index 5d072f3..fbb004b 100644 --- a/src/main/java/com/hibitbackendimproved/profile/domain/Profile.java +++ b/src/main/java/com/hibitbackendimproved/profile/domain/Profile.java @@ -5,10 +5,20 @@ import com.hibitbackendimproved.member.domain.Member; import com.hibitbackendimproved.profile.exception.InvalidIntroduceException; import com.hibitbackendimproved.profile.exception.InvalidNicknameException; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.Builder; import lombok.Getter; -import javax.persistence.*; @Getter @Table(name = "profiles") @Entity @@ -127,6 +137,7 @@ public void updateIntroduce(final String introduce) { validateIntroduce(introduce); this.introduce = introduce; } + public void updateImageName(final String imageName) { this.imageName = imageName; } diff --git a/src/main/java/com/hibitbackendimproved/profile/domain/ProfileRepository.java b/src/main/java/com/hibitbackendimproved/profile/domain/ProfileRepository.java index db2ef1f..3fa241c 100644 --- a/src/main/java/com/hibitbackendimproved/profile/domain/ProfileRepository.java +++ b/src/main/java/com/hibitbackendimproved/profile/domain/ProfileRepository.java @@ -9,7 +9,7 @@ public interface ProfileRepository extends JpaRepository { - default Profile getById(final Long id) { + default Profile getByIdOrThrow(final Long id) { return this.findById(id) .orElseThrow(NotFoundProfileException::new); } diff --git a/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileCreateRequest.java b/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileCreateRequest.java index 753ce3a..622e84a 100644 --- a/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileCreateRequest.java +++ b/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileCreateRequest.java @@ -5,13 +5,13 @@ import com.hibitbackendimproved.profile.domain.AddressDistrict; import com.hibitbackendimproved.profile.domain.PersonalityType; import com.hibitbackendimproved.profile.domain.Profile; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) diff --git a/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileUpdateRequest.java b/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileUpdateRequest.java index 431c9f6..fdaf596 100644 --- a/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileUpdateRequest.java +++ b/src/main/java/com/hibitbackendimproved/profile/dto/request/ProfileUpdateRequest.java @@ -3,13 +3,13 @@ import com.hibitbackendimproved.profile.domain.AddressCity; import com.hibitbackendimproved.profile.domain.AddressDistrict; import com.hibitbackendimproved.profile.domain.PersonalityType; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) diff --git a/src/main/java/com/hibitbackendimproved/profile/exception/NotFoundAddressCityException.java b/src/main/java/com/hibitbackendimproved/profile/exception/NotFoundAddressCityException.java index d3585c7..8af0682 100644 --- a/src/main/java/com/hibitbackendimproved/profile/exception/NotFoundAddressCityException.java +++ b/src/main/java/com/hibitbackendimproved/profile/exception/NotFoundAddressCityException.java @@ -4,6 +4,7 @@ public class NotFoundAddressCityException extends RuntimeException { public NotFoundAddressCityException(final String message) { super(message); } + public NotFoundAddressCityException() { this("존재하지 않는 도시입니다."); } diff --git a/src/main/java/com/hibitbackendimproved/profile/presentation/ProfileController.java b/src/main/java/com/hibitbackendimproved/profile/presentation/ProfileController.java index 7c7de52..ad7ab99 100644 --- a/src/main/java/com/hibitbackendimproved/profile/presentation/ProfileController.java +++ b/src/main/java/com/hibitbackendimproved/profile/presentation/ProfileController.java @@ -3,18 +3,23 @@ import com.hibitbackendimproved.auth.dto.LoginMember; import com.hibitbackendimproved.auth.presentation.AuthenticationPrincipal; -import com.hibitbackendimproved.support.ApiResponse; import com.hibitbackendimproved.profile.application.ProfileService; import com.hibitbackendimproved.profile.domain.PersonalityType; import com.hibitbackendimproved.profile.dto.request.ProfileCreateRequest; import com.hibitbackendimproved.profile.dto.request.ProfileUpdateRequest; import com.hibitbackendimproved.profile.dto.response.ProfileOtherResponse; import com.hibitbackendimproved.profile.dto.response.ProfileResponse; +import com.hibitbackendimproved.support.ApiResponse; +import jakarta.validation.Valid; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; import java.util.Arrays; import java.util.List; @@ -28,7 +33,7 @@ public ProfileController(ProfileService profileService) { @PostMapping("/api/profiles/new") public ResponseEntity> saveMyProfile(@AuthenticationPrincipal final LoginMember loginMember, - @Valid @RequestBody final ProfileCreateRequest request) { + @Valid @RequestBody final ProfileCreateRequest request) { ProfileResponse profileResponse = profileService.save(loginMember.getId(), request); ApiResponse apiResponse = ApiResponse.created(profileResponse); return new ResponseEntity<>(apiResponse, HttpStatus.CREATED); @@ -50,7 +55,7 @@ public ResponseEntity> findMyProfile(@Authenticatio @GetMapping("/api/profiles/other/{id}") public ResponseEntity> findOtherProfile(@AuthenticationPrincipal final LoginMember loginMember, - @PathVariable(name = "id") final Long otherMemberId) { + @PathVariable(name = "id") final Long otherMemberId) { ProfileOtherResponse profileOtherResponse = profileService.findOtherProfile(otherMemberId); ApiResponse apiResponse = ApiResponse.ok(profileOtherResponse); return new ResponseEntity<>(apiResponse, HttpStatus.OK); @@ -58,7 +63,7 @@ public ResponseEntity> findOtherProfile(@Authe @PutMapping("/api/profiles/me") public ResponseEntity> update(@AuthenticationPrincipal final LoginMember loginMember, - @Valid @RequestBody final ProfileUpdateRequest request) { + @Valid @RequestBody final ProfileUpdateRequest request) { profileService.update(loginMember.getId(), request); ApiResponse apiResponse = ApiResponse.noContent(); return new ResponseEntity<>(apiResponse, HttpStatus.NO_CONTENT); diff --git a/src/main/java/com/hibitbackendimproved/support/error/dto/ErrorReportRequest.java b/src/main/java/com/hibitbackendimproved/support/error/dto/ErrorReportRequest.java index 814c67c..e5d6842 100644 --- a/src/main/java/com/hibitbackendimproved/support/error/dto/ErrorReportRequest.java +++ b/src/main/java/com/hibitbackendimproved/support/error/dto/ErrorReportRequest.java @@ -1,6 +1,7 @@ package com.hibitbackendimproved.support.error.dto; -import javax.servlet.http.HttpServletRequest; + +import jakarta.servlet.http.HttpServletRequest; public class ErrorReportRequest { diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-local.yml similarity index 66% rename from src/main/resources/application-test.yml rename to src/main/resources/application-local.yml index 3b2be62..8902a85 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-local.yml @@ -1,6 +1,6 @@ spring: datasource: - url: jdbc:h2:mem:db?serverTimezone=Asia/Seoul;MODE=MYSQL; + url: jdbc:h2:mem:db;MODE=MySQL;DB_CLOSE_DELAY=-1 driver-class-name: org.h2.Driver username: sa password: @@ -8,7 +8,7 @@ spring: jpa: open-in-view: false hibernate: - ddl-auto: none # JPA Setting + ddl-auto: none show_sql: true properties: dialect: org.hibernate.dialect.MySQL8Dialect @@ -41,22 +41,22 @@ server: max-request-size: 10MB -# Google OAuth +# OAuth cors: allow-origin: - urls: http://localhost:8080, http://localhost:3000, http://localhost:3000/google-callback + urls: http://localhost:8080, http://localhost:3000, http://localhost:3000/oauth/kakao-redirect oauth: - google: - client-id: ${GOOGLE_CLIENT_ID} - client-secret: ${GOOGLE_CLIENT_SECRET} - oauth-end-point: https://accounts.google.com/o/oauth2/v2/auth + kakao: + client-id: ${KAKAO_CLIENT_ID} + client-secret: ${KAKAO_CLIENT_SECRET} + authorization-endpoint: https://kauth.kakao.com/oauth/authorize response-type: code scopes: - - https://www.googleapis.com/auth/userinfo.email - - https://www.googleapis.com/auth/userinfo.profile - token-uri: https://oauth2.googleapis.com/token - access-type: offline + - profile_nickname + - profile_image + token-uri: https://kauth.kakao.com/oauth/token # 액세스 토큰 받을 때 필요한 endpoint + user-info-uri: https://kapi.kakao.com/v2/user/me # 액세스 토큰을 사용하여 이 endpoint 를 호출하여 사용자의 정보를 가져올 때 사용 security: jwt: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 22b5530..ae8edb2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,4 @@ spring: profiles: include: - - prod + - local diff --git a/src/test/java/com/hibitbackendimproved/auth/application/AuthServiceTest.java b/src/test/java/com/hibitbackendimproved/auth/application/AuthServiceTest.java index 35b542b..8cf390b 100644 --- a/src/test/java/com/hibitbackendimproved/auth/application/AuthServiceTest.java +++ b/src/test/java/com/hibitbackendimproved/auth/application/AuthServiceTest.java @@ -8,6 +8,7 @@ import com.hibitbackendimproved.auth.dto.response.AccessTokenResponse; import com.hibitbackendimproved.auth.event.MemberSavedEvent; import com.hibitbackendimproved.auth.exception.InvalidTokenException; +import com.hibitbackendimproved.auth.exception.ServerErrorOAuthException; import com.hibitbackendimproved.member.domain.Member; import com.hibitbackendimproved.member.domain.MemberRepository; import org.junit.jupiter.api.AfterEach; @@ -44,11 +45,34 @@ void tearDown() { tokenRepository.deleteAll(); } + @Test + void 지원하지_않은_OAuth_Provider로_URI를_생성하면_예외를_던진다() { + // given + final String invalidProvider = "naver"; + final String redirectUri = "https://hibit.site/oauth"; + + // when & then + assertThatThrownBy(() -> authService.generateOAuthUri(invalidProvider, redirectUri)) + .isInstanceOf(ServerErrorOAuthException.class); + } + + @Test + void 지원하지_않는_OAuth_Provider로_로그인을_시도하면_예외를_던진다() { + // given + final String invalidProvider = "naver"; + final String code = "dummy-code"; + final String redirectUri = "https://hibit.site/oauth"; + + // when & then + assertThatThrownBy(() -> authService.handleOAuth(invalidProvider, code, redirectUri)) + .isInstanceOf(ServerErrorOAuthException.class); + } + @DisplayName("토큰 생성을 하면 OAuth 서버에서 인증 후 토큰을 반환한다") @Test void 토큰_생성을_하면_OAuth_서버에서_인증_후_토큰들을_반환한다() { // given & when - AccessAndRefreshTokenResponse actual = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); + final AccessAndRefreshTokenResponse actual = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); // then assertAll(() -> { @@ -57,6 +81,7 @@ void tearDown() { assertThat(events.stream(MemberSavedEvent.class).count()).isEqualTo(1); }); } + @DisplayName("이미 가입된 회원에 대한 Authorization Code를 전달받으면 추가로 회원이 생성되지 않는다") @Test void 이미_가입된_회원에_대한_Authorization_Code를_전달받으면_추가로_회원이_생성되지_않는다() { @@ -76,13 +101,11 @@ void tearDown() { @DisplayName("이미 가입된 회원이고 저장된 RefreshToken이 있으면, 저장된 RefreshToken을 반환한다.") @Test void 이미_가입된_회원이고_저장된_RefreshToken이_있으면_저장된_RefreshToken을_반환한다() { - // 이미 가입된 회원이 소셜 로그인 버튼을 클릭했을 경우엔 회원가입 과정이 생략되고, 곧바로 access token과 refreshtoken이 발급되어야 한다. - // given - AccessAndRefreshTokenResponse response = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); + final AccessAndRefreshTokenResponse response = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); // when - AccessAndRefreshTokenResponse actual = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); + final AccessAndRefreshTokenResponse actual = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); // then assertThat(actual.getRefreshToken()).isEqualTo(response.getRefreshToken()); @@ -92,8 +115,8 @@ void tearDown() { @Test void 리프레시_토큰으로_새로운_엑세스_토큰을_발급한다() { // given - AccessAndRefreshTokenResponse response = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); - TokenRenewalRequest tokenRenewalRequest = new TokenRenewalRequest(response.getRefreshToken()); + final AccessAndRefreshTokenResponse response = authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); + final TokenRenewalRequest tokenRenewalRequest = new TokenRenewalRequest(response.getRefreshToken()); // when AccessTokenResponse accessTokenResponse = authService.generateAccessToken(tokenRenewalRequest); @@ -101,12 +124,13 @@ void tearDown() { // then assertThat(accessTokenResponse.getAccessToken()).isNotEmpty(); } + @DisplayName("리프레시 토큰으로 새로운 엑세스 토큰을 발급 할 때, 리프레시 토큰이 존재하지 않으면 예외를 던진다.") @Test void 리프레시_토큰으로_새로운_엑세스_토큰을_발급_할_때_리프레시_토큰이_존재하지_않으면_예외를_던진다() { // given authService.generateAccessAndRefreshToken(MEMBER.getOAuthMember()); - TokenRenewalRequest tokenRenewalRequest = new TokenRenewalRequest("DummyRefreshToken"); + final TokenRenewalRequest tokenRenewalRequest = new TokenRenewalRequest("DummyRefreshToken"); // when & then assertThatThrownBy(() -> authService.generateAccessToken(tokenRenewalRequest)) diff --git a/src/test/java/com/hibitbackendimproved/auth/domain/AuthTokenTest.java b/src/test/java/com/hibitbackendimproved/auth/domain/AuthTokenTest.java index 7b4e6f7..cdd22c3 100644 --- a/src/test/java/com/hibitbackendimproved/auth/domain/AuthTokenTest.java +++ b/src/test/java/com/hibitbackendimproved/auth/domain/AuthTokenTest.java @@ -12,7 +12,7 @@ class AuthTokenTest { @Test void 같은_리프레시_토큰_값이면_정상적으로_메서드를_종료한다() { // given - AuthToken authToken = new AuthToken("dummyAccessToken", "dummyRefreshToken", 0); + AuthToken authToken = new AuthToken("dummyAccessToken", "dummyRefreshToken"); // when & then authToken.validateHasSameRefreshToken(authToken.getRefreshToken()); @@ -22,7 +22,7 @@ class AuthTokenTest { @Test void 같은_리프레시_토큰_값이_아니면_예외를_발생한다() { // given - AuthToken authToken = new AuthToken("dummyAccessToken", "dummyRefreshToken", 0); + AuthToken authToken = new AuthToken("dummyAccessToken", "dummyRefreshToken"); // when & then assertThatThrownBy(() -> authToken.validateHasSameRefreshToken("invalidRefreshToken")) diff --git a/src/test/java/com/hibitbackendimproved/auth/presentation/AuthControllerTest.java b/src/test/java/com/hibitbackendimproved/auth/presentation/AuthControllerTest.java index ff01bc4..0837ea8 100644 --- a/src/test/java/com/hibitbackendimproved/auth/presentation/AuthControllerTest.java +++ b/src/test/java/com/hibitbackendimproved/auth/presentation/AuthControllerTest.java @@ -2,21 +2,22 @@ import com.hibitbackendimproved.ControllerTestSupport; import com.hibitbackendimproved.auth.dto.LoginMember; +import com.hibitbackendimproved.auth.dto.OAuthMember; import com.hibitbackendimproved.auth.exception.InvalidTokenException; -import com.hibitbackendimproved.infrastructure.oauth.exception.OAuthException; +import com.hibitbackendimproved.auth.exception.OAuthException; +import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.restdocs.payload.JsonFieldType; -import javax.servlet.http.Cookie; - import static com.hibitbackendimproved.common.AuthFixtures.*; import static com.hibitbackendimproved.common.fixtures.MemberFixtures.FANCY_ID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willDoNothing; +import static org.mockito.Mockito.mock; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; @@ -34,19 +35,19 @@ class AuthControllerTest extends ControllerTestSupport { @Test void OAuth_소셜_로그인을_위한_링크와_상태코드_200을_반환한다() throws Exception { // given - given(oAuthUri.generate(any())).willReturn(OAuth_로그인_링크); + given(authService.generateOAuthUri(any(), any())).willReturn(OAuth_로그인_링크); // when & then - mockMvc.perform(get("/api/auth/{oauthProvider}/oauth-uri?redirectUri={redirectUri}", GOOGLE_PROVIDER, + mockMvc.perform(get("/api/v1/auth/{oauthProvider}/oauth-uri?redirectUri={redirectUri}", KAKAO_PROVIDER, "https://hibit.com/oauth")) .andDo(print()) .andDo(document("auth/generate/redirectUri/success", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( - parameterWithName("oauthProvider").description("OAuth 로그인 제공자 (GOOGLE)") + parameterWithName("oauthProvider").description("OAuth 로그인 제공자 (KAKAO)") ), - requestParameters( + queryParameters( parameterWithName("redirectUri").description("OAuth Redirect URI") ), responseFields( @@ -62,10 +63,11 @@ class AuthControllerTest extends ControllerTestSupport { @Test void OAuth_구글_로그인을_하면_accessToken과_refreshToken_값과_상태코드_200을_반환한다() throws Exception { // given + given(authService.handleOAuth(any(), any(), any())).willReturn(mock(OAuthMember.class)); given(authService.generateAccessAndRefreshToken(any())).willReturn(MEMBER_인증_코드_토큰_응답()); // when & then - mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/{oauthProvider}/token", OAUTH_PROVIDER) + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/auth/{oauthProvider}/token", OAUTH_PROVIDER) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(MEMBER_인증_코드_토큰_요청()))) @@ -74,7 +76,7 @@ class AuthControllerTest extends ControllerTestSupport { preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( - parameterWithName("oauthProvider").description("OAuth 로그인 제공자(GOOGLE)") + parameterWithName("oauthProvider").description("OAuth 로그인 제공자(KAKAO)") ), requestFields( fieldWithPath("code").type(JsonFieldType.STRING).description("OAuth 로그인 인증 코드"), @@ -85,8 +87,7 @@ class AuthControllerTest extends ControllerTestSupport { fieldWithPath("meta.code").type(JsonFieldType.NUMBER).description("응답 코드"), fieldWithPath("meta.message").type(JsonFieldType.STRING).description("응답 메시지"), fieldWithPath("data.accessToken").type(JsonFieldType.STRING).description("히빗 Access Token"), - fieldWithPath("data.refreshToken").type(JsonFieldType.STRING).description("히빗 Refresh Token"), - fieldWithPath("data.isProfileRegistered").type(JsonFieldType.NUMBER).description("프로필 등록 여부") + fieldWithPath("data.refreshToken").type(JsonFieldType.STRING).description("히빗 Refresh Token") ) )) .andExpect(status().isOk()); @@ -96,10 +97,9 @@ class AuthControllerTest extends ControllerTestSupport { @Test void OAuth_로그인_과정에서_Resource_Server_에러가_발생하면_상태코드_500을_반환한다() throws Exception { // given - given(authService.generateAccessAndRefreshToken(any())).willThrow(new OAuthException()); - + given(authService.handleOAuth(any(), any(), any())).willThrow(new OAuthException("Oauth 서버와의 통신 과정에서 문제가 발생했습니다.")); // when & then - mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/{oauthProvider}/token", OAUTH_PROVIDER) + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/auth/{oauthProvider}/token", OAUTH_PROVIDER) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(MEMBER_인증_코드_토큰_요청()))) @@ -108,7 +108,7 @@ class AuthControllerTest extends ControllerTestSupport { preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters( - parameterWithName("oauthProvider").description("OAuth 로그인 제공자(GOOGLE)") + parameterWithName("oauthProvider").description("OAuth 로그인 제공자(KAKAO)") ), requestFields( fieldWithPath("code").type(JsonFieldType.STRING).description("OAuth 로그인 인증 코드"), @@ -126,7 +126,7 @@ class AuthControllerTest extends ControllerTestSupport { given(authService.generateAccessToken(any())).willReturn(MEMBER_리뉴얼_토큰_응답()); // when & then - mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/token/access") + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/auth/token/access") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .cookie(new Cookie("refreshToken", "ccccc.bbbbb.aaaaa"))) @@ -145,7 +145,7 @@ class AuthControllerTest extends ControllerTestSupport { given(authService.generateAccessToken(any())).willThrow(new InvalidTokenException()); // when & then - mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/token/access") + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/auth/token/access") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .cookie(new Cookie("refreshToken", "ccccc.bbbbb.aaaaa"))) @@ -165,7 +165,7 @@ class AuthControllerTest extends ControllerTestSupport { willDoNothing().given(authService).deleteToken(loginMember.getId()); // when & then - mockMvc.perform(RestDocumentationRequestBuilders.get("/api/auth/logout") + mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/auth/logout") .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) diff --git a/src/test/java/com/hibitbackendimproved/common/AuthFixtures.java b/src/test/java/com/hibitbackendimproved/common/AuthFixtures.java index b78c07d..6bcb5f3 100644 --- a/src/test/java/com/hibitbackendimproved/common/AuthFixtures.java +++ b/src/test/java/com/hibitbackendimproved/common/AuthFixtures.java @@ -7,30 +7,26 @@ public class AuthFixtures { - public static final String GOOGLE_PROVIDER = "google"; + public static final String KAKAO_PROVIDER = "kakao"; public static final String OAUTH_PROVIDER = "oauthProvider"; public static final String STUB_MEMBER_인증_코드 = "member authorization code"; public static final String STUB_MEMBER_REFRESH_인증_코드 = "member refresh authorization code"; - public static final int STUB_MEMBER_PROFILE_NOT_REGISTER = 0; - public static final String STUB_CREATOR_인증_코드 = "creator authorization code"; - public static final String 더미_엑세스_토큰 = "aaaaa.bbbbb.ccccc"; public static final String 더미_리프레시_토큰 = "ccccc.bbbbb.aaaaa"; - public static final String OAuth_로그인_링크 = "https://accounts.google.com/o/oauth2/v2/auth"; - public static final String MEMBER_이메일 = "member@email.com"; + public static final String OAuth_로그인_링크 = "https://kauth.kakao.com/oauth/authorize?client_id=test_client_id&redirect_uri=https://hibit.site/oauth&response_type=code"; public static final String 더미_시크릿_키 = "fancykijuhnbmsowishcxbzcsdjsajdabwcksjadaksdhabdsadasjkdb"; public static final String STUB_OAUTH_ACCESS_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; public static TokenRequest MEMBER_인증_코드_토큰_요청() { - return new TokenRequest(STUB_MEMBER_인증_코드, "https://hibit2.com/oauth"); + return new TokenRequest(STUB_MEMBER_인증_코드, "https://hibit.site/oauth"); } public static AccessAndRefreshTokenResponse MEMBER_인증_코드_토큰_응답() { - return new AccessAndRefreshTokenResponse(STUB_MEMBER_인증_코드, STUB_MEMBER_REFRESH_인증_코드, STUB_MEMBER_PROFILE_NOT_REGISTER); + return new AccessAndRefreshTokenResponse(STUB_MEMBER_인증_코드, STUB_MEMBER_REFRESH_인증_코드); } public static TokenRenewalRequest MEMBER_리뉴얼_토큰_요청() { diff --git a/src/test/java/com/hibitbackendimproved/common/DatabaseCleaner.java b/src/test/java/com/hibitbackendimproved/common/DatabaseCleaner.java index 622a2c1..8ff4aa7 100644 --- a/src/test/java/com/hibitbackendimproved/common/DatabaseCleaner.java +++ b/src/test/java/com/hibitbackendimproved/common/DatabaseCleaner.java @@ -1,11 +1,11 @@ package com.hibitbackendimproved.common; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Table; +import jakarta.persistence.metamodel.Type; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; -import javax.persistence.Table; -import javax.persistence.metamodel.Type; import java.util.List; import java.util.stream.Collectors; @@ -13,7 +13,6 @@ public class DatabaseCleaner { private final EntityManager entityManager; - private final List tableNames; public DatabaseCleaner(final EntityManager entityManager) { diff --git a/src/test/java/com/hibitbackendimproved/common/builder/GivenBuilder.java b/src/test/java/com/hibitbackendimproved/common/builder/GivenBuilder.java index cca4eec..bad4f2c 100644 --- a/src/test/java/com/hibitbackendimproved/common/builder/GivenBuilder.java +++ b/src/test/java/com/hibitbackendimproved/common/builder/GivenBuilder.java @@ -14,7 +14,7 @@ public GivenBuilder(BuilderSupporter bs) { } public GivenBuilder 회원_가입을_한다(final String email, final String displayName) { - Member member = new Member(email, displayName, SocialType.GOOGLE); + Member member = new Member(email, displayName, SocialType.KAKAO); this.member = bs.memberRepository().save(member); OAuthToken oAuthToken = new OAuthToken(this.member, "refreshTokenValue"); bs.oAuthTokenRepository().save(oAuthToken); diff --git a/src/test/java/com/hibitbackendimproved/common/fixtures/MemberFixtures.java b/src/test/java/com/hibitbackendimproved/common/fixtures/MemberFixtures.java index 2438ab3..c0eb9bb 100644 --- a/src/test/java/com/hibitbackendimproved/common/fixtures/MemberFixtures.java +++ b/src/test/java/com/hibitbackendimproved/common/fixtures/MemberFixtures.java @@ -10,41 +10,14 @@ public class MemberFixtures { public static final Long FANCY_ID = 1L; public static final String 팬시_이메일 = "fancy@gmail.com"; public static final String 팬시_닉네임 = "팬시"; - public static final SocialType 소셜로그인유형 = SocialType.GOOGLE; - - public static final boolean 팬시_프로필여부 = true; - - public static final MemberResponse 팬시_응답 = new MemberResponse(1L, 팬시_이메일, 팬시_닉네임, 소셜로그인유형 , 팬시_프로필여부); - - /* 브루스 */ - public static final String 브루스_이메일 = "fancy@gmail.com"; - public static final String 브루스_닉네임 = "팬시"; - - /* 데이브 */ - public static final String 데이브_이메일 = "fancy@gmail.com"; - public static final String 데이브_닉네임 = "팬시"; + public static final SocialType 소셜로그인유형 = SocialType.KAKAO; + public static final MemberResponse 팬시_응답 = new MemberResponse(1L, 팬시_이메일, 팬시_닉네임, 소셜로그인유형); public static Member 팬시() { return Member.builder() .email(팬시_이메일) - .displayName(팬시_닉네임) - .socialType(소셜로그인유형) - .build(); - } - - public static Member 브루스() { - return Member.builder() - .email(브루스_이메일) - .displayName(브루스_닉네임) - .socialType(소셜로그인유형) - .build(); - } - - public static Member 데이브() { - return Member.builder() - .email(데이브_이메일) - .displayName(데이브_닉네임) + .nickname(팬시_닉네임) .socialType(소셜로그인유형) .build(); } diff --git a/src/test/java/com/hibitbackendimproved/common/fixtures/PostFixtures.java b/src/test/java/com/hibitbackendimproved/common/fixtures/PostFixtures.java index 775ddd6..93d9353 100644 --- a/src/test/java/com/hibitbackendimproved/common/fixtures/PostFixtures.java +++ b/src/test/java/com/hibitbackendimproved/common/fixtures/PostFixtures.java @@ -26,7 +26,7 @@ public class PostFixtures { public static final String 전시회제목1 = "PROJECT HASHTAG 2023 SELECTED ARTISTS"; public static final int 전시관람인원1 = 3; - public static final LocalDateTime 전시관람희망날짜1 = LocalDateTime.now(); + public static final LocalDateTime 전시관람희망날짜1 = LocalDateTime.now().withNano(0); public static final String 오픈채팅방Url1 = "http://projecthashtag.net/"; public static final TogetherActivity 함께하고싶은활동1 = TogetherActivity.EAT; @@ -56,7 +56,7 @@ public class PostFixtures { public static final String 전시회제목2 = "오스틴리 전시회"; public static final int 전시관람인원2 = 4; - public static final LocalDateTime 전시관람희망날짜2 = LocalDateTime.now(); + public static final LocalDateTime 전시관람희망날짜2 = LocalDateTime.now().plusDays(1).withNano(0); public static final String 오픈채팅방Url2 = "http://ostin.net/"; public static final TogetherActivity 함께하고싶은활동2 = TogetherActivity.CAFE; diff --git a/src/test/java/com/hibitbackendimproved/infrastructure/oauth/client/StubOAuthClient.java b/src/test/java/com/hibitbackendimproved/infrastructure/oauth/client/StubOAuthClient.java index 80c1638..a16fb37 100644 --- a/src/test/java/com/hibitbackendimproved/infrastructure/oauth/client/StubOAuthClient.java +++ b/src/test/java/com/hibitbackendimproved/infrastructure/oauth/client/StubOAuthClient.java @@ -16,7 +16,7 @@ public OAuthMember getOAuthMember(final String code, final String redirectUri) { } @Override - public OAuthAccessTokenResponse getAccessToken(final String refreshToken) { - return new OAuthAccessTokenResponse(STUB_OAUTH_ACCESS_TOKEN); + public String getProviderName() { + return ""; } } diff --git a/src/test/java/com/hibitbackendimproved/infrastructure/oauth/uri/StubOAuthUri.java b/src/test/java/com/hibitbackendimproved/infrastructure/oauth/uri/StubOAuthUri.java index c14374c..5290f7b 100644 --- a/src/test/java/com/hibitbackendimproved/infrastructure/oauth/uri/StubOAuthUri.java +++ b/src/test/java/com/hibitbackendimproved/infrastructure/oauth/uri/StubOAuthUri.java @@ -9,4 +9,9 @@ public class StubOAuthUri implements OAuthUri { public String generate(final String redirectUri) { return "https://localhost:3000"; } + + @Override + public String getProviderName() { + return "oauth"; + } } diff --git a/src/test/java/com/hibitbackendimproved/member/application/MemberServiceTest.java b/src/test/java/com/hibitbackendimproved/member/application/MemberServiceTest.java index 23b16cd..fe8a617 100644 --- a/src/test/java/com/hibitbackendimproved/member/application/MemberServiceTest.java +++ b/src/test/java/com/hibitbackendimproved/member/application/MemberServiceTest.java @@ -49,7 +49,7 @@ void tearDown() { @Test void 서비스에_가입한_회원을_조회한다_수정전() { // given - Member member = new Member("fancy@gmail.com", "fancy", SocialType.GOOGLE); + Member member = new Member("fancy@gmail.com", "fancy", SocialType.KAKAO); this.member = memberRepository.save(member); OAuthToken oAuthToken = new OAuthToken(this.member, "refreshTokenValue"); oAuthTokenRepository.save(oAuthToken); diff --git a/src/test/java/com/hibitbackendimproved/member/domain/MemberRepositoryTest.java b/src/test/java/com/hibitbackendimproved/member/domain/MemberRepositoryTest.java index dc863a8..5ef06d4 100644 --- a/src/test/java/com/hibitbackendimproved/member/domain/MemberRepositoryTest.java +++ b/src/test/java/com/hibitbackendimproved/member/domain/MemberRepositoryTest.java @@ -26,7 +26,7 @@ class MemberRepositoryTest extends IntegrationTestSupport { Member 팬시 = memberRepository.save(팬시()); // when - Member actual = memberRepository.getByEmail(팬시_이메일); + Member actual = memberRepository.getByEmailOrThrow(팬시_이메일); // then assertThat(actual.getId()).isEqualTo(팬시.getId()); @@ -52,7 +52,7 @@ class MemberRepositoryTest extends IntegrationTestSupport { String email = "devfancy@gmail.com"; // given & when & then - assertThatThrownBy(() -> memberRepository.getByEmail(email)) + assertThatThrownBy(() -> memberRepository.getByEmailOrThrow(email)) .isInstanceOf(NotFoundMemberException.class); } diff --git a/src/test/java/com/hibitbackendimproved/member/domain/MemberTest.java b/src/test/java/com/hibitbackendimproved/member/domain/MemberTest.java index 1dec88f..80cc815 100644 --- a/src/test/java/com/hibitbackendimproved/member/domain/MemberTest.java +++ b/src/test/java/com/hibitbackendimproved/member/domain/MemberTest.java @@ -8,6 +8,7 @@ import static com.hibitbackendimproved.common.fixtures.MemberFixtures.팬시_닉네임; import static com.hibitbackendimproved.common.fixtures.MemberFixtures.팬시_이메일; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -17,7 +18,7 @@ class MemberTest { void 회원을_생성한다() { // given & when & then - assertDoesNotThrow(() -> new Member(팬시_이메일, 팬시_닉네임, SocialType.GOOGLE)); + assertDoesNotThrow(() -> new Member(팬시_이메일, 팬시_닉네임, SocialType.KAKAO)); } @DisplayName("회원의 email 형식이 맞지 않으면 예외가 발생한다.") @@ -26,27 +27,41 @@ class MemberTest { void 회원의_email_형식이_맞지_않으면_예외가_발생한다(final String email) { // given & when & then - assertThatThrownBy(() -> new Member(email, 팬시_닉네임, SocialType.GOOGLE)) + assertThatThrownBy(() -> new Member(email, 팬시_닉네임, SocialType.KAKAO)) .isInstanceOf(InvalidMemberException.class); } - @DisplayName("회원의 닉네임 형식이 빈칸이거나 공백이면 예외가 발생한다.") + @DisplayName("회원의 닉네임을 수정한다.") + @Test + void 회원의_닉네임을_수정한다() { + // given + final Member member = new Member(팬시_이메일, 팬시_닉네임, SocialType.KAKAO); + final String updatedNickname = "잠만보"; + + // when + member.updateNickname(updatedNickname); + + // then + assertThat(member.getNickname()).isEqualTo(updatedNickname); + } + + @DisplayName("회원의 닉네임 형식이 잘못되면 예외가 발생한다.") @ParameterizedTest @ValueSource(strings = {"", " "}) - void 회원의_닉네임_형식이_빈칸이거나_공백이면_예외가_발생한다(final String displayName) { + void 회원의_닉네임_형식이_빈칸이거나_공백이면_예외가_발생한다(final String nickname) { // given & when & then - assertThatThrownBy(() -> new Member(팬시_이메일, displayName, SocialType.GOOGLE)) + assertThatThrownBy(() -> new Member(팬시_이메일, nickname, SocialType.KAKAO)) .isInstanceOf(InvalidMemberException.class); } @DisplayName("회원의 닉네임 글자가 20자 초과하면 예외가 발생한다.") @ParameterizedTest @ValueSource(strings = {"일이삼사오육칠팔구십일이삼사오육칠팔구십일", "일이삼사오육칠팔구십일이삼사오육칠팔구십일이삼사오육칠팔구십"}) - void 회원의_닉네임_글자가_20자_초과하면_예외가_발생한다(final String displayName) { + void 회원의_닉네임_글자가_20자_초과하면_예외가_발생한다(final String nickname) { // given & when & then - assertThatThrownBy(() -> new Member(팬시_이메일, displayName, SocialType.GOOGLE)) + assertThatThrownBy(() -> new Member(팬시_이메일, nickname, SocialType.KAKAO)) .isInstanceOf(InvalidMemberException.class); } } diff --git a/src/test/java/com/hibitbackendimproved/post/application/PostServiceTest.java b/src/test/java/com/hibitbackendimproved/post/application/PostServiceTest.java index 3478699..035daf5 100644 --- a/src/test/java/com/hibitbackendimproved/post/application/PostServiceTest.java +++ b/src/test/java/com/hibitbackendimproved/post/application/PostServiceTest.java @@ -15,6 +15,8 @@ import com.hibitbackendimproved.post.exception.NotFoundPostException; import com.hibitbackendimproved.profile.domain.Profile; import com.hibitbackendimproved.profile.domain.ProfileRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,8 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -72,7 +72,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); profileRepository.save(팬시_프로필); @@ -104,7 +104,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -120,7 +120,7 @@ void tearDown() { assertAll( () -> assertThat(response.getId()).isEqualTo(post.getId()), () -> assertThat(response.getWriterId()).isEqualTo(post.getMember().getId()), - () -> assertThat(response.getWriterName()).isEqualTo(post.getMember().getDisplayName()), + () -> assertThat(response.getWriterName()).isEqualTo(post.getMember().getNickname()), () -> assertThat(response.getTitle()).isEqualTo(post.getTitle()), () -> assertThat(response.getContent()).isEqualTo(post.getContent()), () -> assertThat(response.getExhibition()).isEqualTo(post.getExhibition()) @@ -133,7 +133,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -160,7 +160,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -195,7 +195,7 @@ private static Stream argsOfFindPostViewCount() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -209,7 +209,7 @@ private static Stream argsOfFindPostViewCount() { // when PostsSliceResponse myPosts = postService.searchSlickWithQuery(query, - PageRequest.of(0, 3, DESC, "created_date_time")); + PageRequest.of(0, 3, DESC, "created_at")); PostsCountResponse response = postService.countPostWithQuery(query); // then @@ -227,7 +227,7 @@ private static Stream argsOfFindPostViewCount() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -239,7 +239,7 @@ private static Stream argsOfFindPostViewCount() { // when PostsSliceResponse response = postService.searchSlickWithQuery("프로젝트", - PageRequest.of(0, 3, DESC, "created_date_time")); + PageRequest.of(0, 3, DESC, "created_at")); // then assertAll( @@ -255,7 +255,7 @@ private static Stream argsOfFindPostViewCount() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -269,7 +269,7 @@ private static Stream argsOfFindPostViewCount() { String query = "프로젝트"; // when PostsSliceResponse myPosts = postService.searchSlickWithQuery(query, - PageRequest.of(0, 3, DESC, "created_date_time")); + PageRequest.of(0, 3, DESC, "created_at")); PostsCountResponse response = postService.countPostWithQuery(query); // then @@ -288,7 +288,7 @@ private static Stream argsOfFindPostViewCount() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); @@ -301,7 +301,7 @@ private static Stream argsOfFindPostViewCount() { // when PostsSliceResponse myPosts = postService.searchSlickWithQuery(query, - PageRequest.of(0, 3, DESC, "created_date_time")); + PageRequest.of(0, 3, DESC, "created_at")); PostsCountResponse response = postService.countPostWithQuery(query); // then @@ -346,7 +346,7 @@ private static Stream argsOfFindPostViewCount() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile 팬시_프로필 = 팬시_프로필(member); Profile profile = profileRepository.save(팬시_프로필); diff --git a/src/test/java/com/hibitbackendimproved/post/domain/PostRepositoryTest.java b/src/test/java/com/hibitbackendimproved/post/domain/PostRepositoryTest.java index b645569..f38f7c3 100644 --- a/src/test/java/com/hibitbackendimproved/post/domain/PostRepositoryTest.java +++ b/src/test/java/com/hibitbackendimproved/post/domain/PostRepositoryTest.java @@ -6,6 +6,8 @@ import com.hibitbackendimproved.post.exception.NotFoundPostException; import com.hibitbackendimproved.profile.domain.Profile; import com.hibitbackendimproved.profile.domain.ProfileRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,8 +19,6 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; import java.util.List; import static com.hibitbackendimproved.common.fixtures.MemberFixtures.팬시; @@ -108,7 +108,7 @@ void updateViewCount() { @Test void findPostPagesByQuery() { // given - Page result = postRepository.findPostPagesByQuery(PageRequest.of(0, 3, DESC, "created_date_time"), ""); + Page result = postRepository.findPostPagesByQuery(PageRequest.of(0, 3, DESC, "created_at"), ""); // when & then assertThat(result.getTotalElements()).isEqualTo(3L); @@ -118,7 +118,7 @@ void findPostPagesByQuery() { @Test void findPostSlicePageByQuery() { // given - Slice result = postRepository.findPostSlicePageByQuery(PageRequest.of(0, 2, DESC, "created_date_time"), ""); + Slice result = postRepository.findPostSlicePageByQuery(PageRequest.of(0, 2, DESC, "created_at"), ""); // when & then assertThat(result.getContent()).containsExactly(post2, post1); diff --git a/src/test/java/com/hibitbackendimproved/post/presentation/PostControllerTest.java b/src/test/java/com/hibitbackendimproved/post/presentation/PostControllerTest.java index 633bd90..fd1a43c 100644 --- a/src/test/java/com/hibitbackendimproved/post/presentation/PostControllerTest.java +++ b/src/test/java/com/hibitbackendimproved/post/presentation/PostControllerTest.java @@ -158,7 +158,7 @@ class PostControllerTest extends ControllerTestSupport { PostDetailResponse response = PostDetailResponse.builder() .id(postId) .writerId(팬시.getId()) - .writerName(팬시.getDisplayName()) + .writerName(팬시.getNickname()) .title(게시글제목1) .content(게시글내용1) .exhibition(전시회제목1) diff --git a/src/test/java/com/hibitbackendimproved/profile/application/ProfileServiceTest.java b/src/test/java/com/hibitbackendimproved/profile/application/ProfileServiceTest.java index 3303a7d..215858f 100644 --- a/src/test/java/com/hibitbackendimproved/profile/application/ProfileServiceTest.java +++ b/src/test/java/com/hibitbackendimproved/profile/application/ProfileServiceTest.java @@ -43,7 +43,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); ProfileCreateRequest request = ProfileCreateRequest.builder() .nickname("devFancy") @@ -76,7 +76,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile profile = 팬시_프로필(member); Long memberId = profileRepository.save(profile).getMember().getId(); @@ -113,7 +113,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile profile = 팬시_프로필(member); Long memberId = profileRepository.save(profile).getMember().getId(); @@ -157,7 +157,7 @@ void tearDown() { // given Member 팬시 = 팬시(); memberRepository.save(팬시); - Member member = memberRepository.getById(팬시.getId()); + Member member = memberRepository.getByIdOrThrow(팬시.getId()); Profile profile = 팬시_프로필(member); Long memberId = profileRepository.save(profile).getMember().getId(); diff --git a/src/test/java/com/hibitbackendimproved/profile/domain/ProfileRepositoryTest.java b/src/test/java/com/hibitbackendimproved/profile/domain/ProfileRepositoryTest.java index c81ee4b..400c310 100644 --- a/src/test/java/com/hibitbackendimproved/profile/domain/ProfileRepositoryTest.java +++ b/src/test/java/com/hibitbackendimproved/profile/domain/ProfileRepositoryTest.java @@ -40,7 +40,7 @@ void setUp() { @Test void 프로필과_회원_테이블이_정상적으로_매핑이_된다() { // given - Profile foundProfile = profileRepository.getById(profile1.getId()); + Profile foundProfile = profileRepository.getByIdOrThrow(profile1.getId()); // when & then assertThat(foundProfile.getMember()).isNotNull(); diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index baf18ed..ac24f2a 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -1,6 +1,6 @@ spring: datasource: - url: jdbc:h2:mem:db?serverTimezone=Asia/Seoul;MODE=MYSQL; + url: jdbc:h2:mem:db;MODE=MySQL;DB_CLOSE_DELAY=-1 driver-class-name: org.h2.Driver username: sa password: @@ -41,27 +41,27 @@ server: servlet: context-path: /api -# Google OAuth +# OAuth cors: allow-origin: - urls: http://localhost:8080, http://localhost:3000, http://localhost:3000/google-callback + urls: http://localhost:8080, http://localhost:3000, http://localhost:3000/kakao-callback oauth: - google: - client-id: ${GOOGLE_CLIENT_ID} - client-secret: ${GOOGLE_CLIENT_SECRET} - oauth-end-point: https://accounts.google.com/o/oauth2/v2/auth + kakao: + client-id: dummy-kakao-client-id + client-secret: dummy-kakao-client-secret + authorization-endpoint: https://kauth.kakao.com/oauth/authorize response-type: code scopes: - - https://www.googleapis.com/auth/userinfo.email - - https://www.googleapis.com/auth/userinfo.profile - token-uri: https://oauth2.googleapis.com/token - access-type: offline + - profile_nickname + - profile_image + token-uri: https://kauth.kakao.com/oauth/token # 액세스 토큰 받을 때 필요한 endpoint + user-info-uri: https://kapi.kakao.com/v2/user/me # 액세스 토큰을 사용하여 이 endpoint 를 호출하여 사용자의 정보를 가져올 때 사용 security: jwt: token: - secret-key: ${JWT_SECRET_KEY} + secret-key: hibi-dummy-secret-key-for-test-12345678901234567890 access: expire-length : 3600000 #1시간 refresh: