diff --git a/.gitignore b/.gitignore index 34bb0dfe..9be6eb35 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,8 @@ out/ .kotlin ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +### Resources ### +**/src/main/resources/application-*.yml +**/src/main/resources/google-services.json diff --git a/adapters/in-web/src/main/kotlin/com/pokit/auth/AuthController.kt b/adapters/in-web/src/main/kotlin/com/pokit/auth/AuthController.kt new file mode 100644 index 00000000..0b1830fc --- /dev/null +++ b/adapters/in-web/src/main/kotlin/com/pokit/auth/AuthController.kt @@ -0,0 +1,20 @@ +package com.pokit.auth + +import com.pokit.auth.port.`in`.AuthUseCase +import com.pokit.token.dto.request.SignInRequest +import org.springframework.http.ResponseEntity +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.RestController + +@RestController +@RequestMapping("/api/v1/auth") +class AuthController( + private val authUseCase: AuthUseCase, +) { + @PostMapping("/signin") + fun signIn( + @RequestBody request: SignInRequest, + ) = ResponseEntity.ok(authUseCase.signIn(request)) +} diff --git a/adapters/in-web/src/main/kotlin/com/pokit/auth/filter/CustomAuthenticationFilter.kt b/adapters/in-web/src/main/kotlin/com/pokit/auth/filter/CustomAuthenticationFilter.kt index a3fe7421..77dceef3 100644 --- a/adapters/in-web/src/main/kotlin/com/pokit/auth/filter/CustomAuthenticationFilter.kt +++ b/adapters/in-web/src/main/kotlin/com/pokit/auth/filter/CustomAuthenticationFilter.kt @@ -1,8 +1,8 @@ package com.pokit.auth.filter -import com.pokit.auth.exception.AuthErrorCode import com.pokit.auth.port.`in`.TokenProvider import com.pokit.common.exception.ClientValidationException +import com.pokit.token.exception.AuthErrorCode import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse @@ -19,6 +19,16 @@ import org.springframework.web.filter.OncePerRequestFilter class CustomAuthenticationFilter( private val tokenProvider: TokenProvider, ) : OncePerRequestFilter() { + override fun shouldNotFilter(request: HttpServletRequest): Boolean { + val excludePath = arrayOf("/api/v1/auth/signin") + val path = request.requestURI + val shouldNotFilter = + excludePath + .any { it.equals(path) } + + return shouldNotFilter + } + override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, @@ -38,7 +48,7 @@ class CustomAuthenticationFilter( private fun getAuthentication(request: HttpServletRequest): Authentication? { val header = request.getHeader(HttpHeaders.AUTHORIZATION) - if(!StringUtils.hasText(header)) { + if (!StringUtils.hasText(header)) { throw ClientValidationException(AuthErrorCode.TOKEN_REQUIRED) } diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/impl/RefreshTokenAdapter.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/impl/RefreshTokenAdapter.kt index 5c9145b7..e43269e9 100644 --- a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/impl/RefreshTokenAdapter.kt +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/impl/RefreshTokenAdapter.kt @@ -1,28 +1,28 @@ package com.pokit.out.persistence.auth.impl -import com.pokit.auth.port.out.RefreshTokenRepository +import com.pokit.auth.port.out.RefreshTokenPort import com.pokit.out.persistence.auth.persist.RefreshTokenJpaEntity -import com.pokit.out.persistence.auth.persist.RefreshTokenJpaRepository +import com.pokit.out.persistence.auth.persist.RefreshTokenRepository import com.pokit.out.persistence.auth.persist.toDomain import com.pokit.token.model.RefreshToken import org.springframework.stereotype.Repository @Repository class RefreshTokenAdapter( - private val refreshTokenJpaRepository: RefreshTokenJpaRepository, -) : RefreshTokenRepository { - override fun save(refreshToken: RefreshToken): RefreshToken { + private val refreshTokenRepository: RefreshTokenRepository, +) : RefreshTokenPort { + override fun persist(refreshToken: RefreshToken): RefreshToken { val refreshTokenEntity = RefreshTokenJpaEntity.of(refreshToken) - val savedToken = refreshTokenJpaRepository.save(refreshTokenEntity) + val savedToken = refreshTokenRepository.save(refreshTokenEntity) return savedToken.toDomain() } - override fun findByUserId(userId: Long): RefreshToken? { - val refreshToken = refreshTokenJpaRepository.findByUserId(userId) + override fun loadByUserId(userId: Long): RefreshToken? { + val refreshToken = refreshTokenRepository.findByUserId(userId) return refreshToken?.toDomain() } override fun deleteById(id: Long) { - refreshTokenJpaRepository.deleteById(id) + refreshTokenRepository.deleteById(id) } } diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/persist/RefreshTokenJpaRepository.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/persist/RefreshTokenRepository.kt similarity index 67% rename from adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/persist/RefreshTokenJpaRepository.kt rename to adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/persist/RefreshTokenRepository.kt index d628f09e..3fec6a6f 100644 --- a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/persist/RefreshTokenJpaRepository.kt +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/auth/persist/RefreshTokenRepository.kt @@ -2,6 +2,6 @@ package com.pokit.out.persistence.auth.persist import org.springframework.data.jpa.repository.JpaRepository -interface RefreshTokenJpaRepository : JpaRepository { +interface RefreshTokenRepository : JpaRepository { fun findByUserId(userId: Long): RefreshTokenJpaEntity? } diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/impl/UserAdapter.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/impl/UserAdapter.kt new file mode 100644 index 00000000..e660bed6 --- /dev/null +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/impl/UserAdapter.kt @@ -0,0 +1,24 @@ +package com.pokit.out.persistence.user.impl + +import com.pokit.out.persistence.user.persist.UserJpaEntity +import com.pokit.out.persistence.user.persist.UserRepository +import com.pokit.out.persistence.user.persist.toDomain +import com.pokit.user.model.User +import com.pokit.user.port.out.UserPort +import org.springframework.stereotype.Repository + +@Repository +class UserAdapter( + private val userRepository: UserRepository, +) : UserPort { + override fun persist(user: User): User { + val userJpaEntity = UserJpaEntity.of(user) + val savedUser = userRepository.save(userJpaEntity) + return savedUser.toDomain() + } + + override fun loadByEmail(email: String): User? { + val userJpaEntity = userRepository.findByEmail(email) + return userJpaEntity?.toDomain() + } +} diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/persist/UserJpaEntity.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/persist/UserJpaEntity.kt new file mode 100644 index 00000000..30a588ab --- /dev/null +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/persist/UserJpaEntity.kt @@ -0,0 +1,30 @@ +package com.pokit.out.persistence.user.persist + +import com.pokit.user.model.Role +import com.pokit.user.model.User +import jakarta.persistence.* + +@Table(name = "USER") +@Entity +class UserJpaEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + val id: Long = 0L, + @Column(name = "email") + val email: String, + @Column(name = "role") + val role: Role, +) { + companion object { + fun of(user: User) = + UserJpaEntity( + email = user.email, + role = user.role, + ) + } +} + +fun UserJpaEntity.toDomain() = User(id = this.id, email = this.email, role = this.role) + +fun UserJpaEntity.toPrincipalUser() = null // Security Context에 담을 user diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/persist/UserRepository.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/persist/UserRepository.kt new file mode 100644 index 00000000..c2a228d6 --- /dev/null +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/user/persist/UserRepository.kt @@ -0,0 +1,7 @@ +package com.pokit.out.persistence.user.persist + +import org.springframework.data.jpa.repository.JpaRepository + +interface UserRepository : JpaRepository { + fun findByEmail(email: String): UserJpaEntity? +} diff --git a/adapters/out-persistence/src/testFixtures/kotlin/com/pokit/auth/AuthFixture.kt b/adapters/out-persistence/src/testFixtures/kotlin/com/pokit/auth/AuthFixture.kt new file mode 100644 index 00000000..93c8a1ea --- /dev/null +++ b/adapters/out-persistence/src/testFixtures/kotlin/com/pokit/auth/AuthFixture.kt @@ -0,0 +1,15 @@ +package com.pokit.auth + +import com.pokit.token.dto.request.SignInRequest +import com.pokit.token.model.AuthPlatform +import com.pokit.token.model.Token + +class AuthFixture { + companion object { + fun getToken() = Token(accessToken = "at", refreshToken = "rf") + + fun getGoogleSigniInRequest() = SignInRequest(AuthPlatform.GOOGLE.platform, "code") + + fun getInvalidSignInRequest() = SignInRequest("구긍", "code") + } +} diff --git a/adapters/out-persistence/src/testFixtures/kotlin/com/pokit/user/UserFixture.kt b/adapters/out-persistence/src/testFixtures/kotlin/com/pokit/user/UserFixture.kt new file mode 100644 index 00000000..c7f97002 --- /dev/null +++ b/adapters/out-persistence/src/testFixtures/kotlin/com/pokit/user/UserFixture.kt @@ -0,0 +1,13 @@ +package com.pokit.user + +import com.pokit.user.dto.UserInfo +import com.pokit.user.model.Role +import com.pokit.user.model.User + +class UserFixture { + companion object { + fun getUser() = User(1L, "ij@naver.com", Role.USER) + + fun getUserInfo() = UserInfo("ig@naver.com") + } +} diff --git a/adapters/out-web/build.gradle.kts b/adapters/out-web/build.gradle.kts new file mode 100644 index 00000000..946ac0fd --- /dev/null +++ b/adapters/out-web/build.gradle.kts @@ -0,0 +1,20 @@ +import org.springframework.boot.gradle.tasks.bundling.BootJar + +plugins { + id("org.springframework.boot") version "3.3.0" + id("io.spring.dependency-management") version "1.1.5" + kotlin("plugin.spring") version "1.9.24" +} + +dependencies { + implementation(project(":application")) + implementation(project(":domain")) + + implementation("org.springframework.boot:spring-boot-starter") + implementation("com.google.firebase:firebase-admin:8.1.0") +} + +tasks { + withType { enabled = true } + withType { enabled = false } +} diff --git a/adapters/out-web/src/main/kotlin/com/pokit/auth/common/config/FirebaseConfig.kt b/adapters/out-web/src/main/kotlin/com/pokit/auth/common/config/FirebaseConfig.kt new file mode 100644 index 00000000..ccdb6c1c --- /dev/null +++ b/adapters/out-web/src/main/kotlin/com/pokit/auth/common/config/FirebaseConfig.kt @@ -0,0 +1,27 @@ +package com.pokit.auth.common.config + +import com.google.auth.oauth2.GoogleCredentials +import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseOptions +import com.google.firebase.auth.FirebaseAuth +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.io.ClassPathResource + +@Configuration +class FirebaseConfig { + @Bean + fun firebaseApp(): FirebaseApp { + val resource = ClassPathResource("google-services.json") + val serviceAccount = resource.inputStream + + val options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .build() + + return FirebaseApp.initializeApp(options) + } + + @Bean + fun firebaseAuth() = FirebaseAuth.getInstance(firebaseApp()) +} diff --git a/adapters/out-web/src/main/kotlin/com/pokit/auth/impl/GoogleApiAdapter.kt b/adapters/out-web/src/main/kotlin/com/pokit/auth/impl/GoogleApiAdapter.kt new file mode 100644 index 00000000..73a51877 --- /dev/null +++ b/adapters/out-web/src/main/kotlin/com/pokit/auth/impl/GoogleApiAdapter.kt @@ -0,0 +1,18 @@ +package com.pokit.auth.impl + +import com.google.firebase.auth.FirebaseAuth +import com.pokit.auth.port.out.GoogleApiClient +import com.pokit.user.dto.UserInfo +import org.springframework.stereotype.Component + +@Component +class GoogleApiAdapter( + private val firebaseAuth: FirebaseAuth +) : GoogleApiClient{ + override fun getUserInfo(authorizationCode: String): UserInfo { + val decodedToken = verifyIdToken(authorizationCode) + return UserInfo(decodedToken.email) // 로그인 한 사용자의 이메일 + } + + private fun verifyIdToken(idToken: String) = firebaseAuth.verifyIdToken(idToken) +} diff --git a/application/build.gradle.kts b/application/build.gradle.kts index 4de2db8f..f8f0edd2 100644 --- a/application/build.gradle.kts +++ b/application/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { // 테스팅 testImplementation("io.mockk:mockk:1.13.7") + testImplementation(testFixtures(project(":adapters:out-persistence"))) } tasks { diff --git a/application/src/main/kotlin/com/pokit/auth/port/in/AuthUseCase.kt b/application/src/main/kotlin/com/pokit/auth/port/in/AuthUseCase.kt new file mode 100644 index 00000000..ca014009 --- /dev/null +++ b/application/src/main/kotlin/com/pokit/auth/port/in/AuthUseCase.kt @@ -0,0 +1,8 @@ +package com.pokit.auth.port.`in` + +import com.pokit.token.dto.request.SignInRequest +import com.pokit.token.model.Token + +interface AuthUseCase { + fun signIn(request: SignInRequest): Token +} diff --git a/application/src/main/kotlin/com/pokit/auth/port/out/GoogleApiClient.kt b/application/src/main/kotlin/com/pokit/auth/port/out/GoogleApiClient.kt new file mode 100644 index 00000000..7ba66dcd --- /dev/null +++ b/application/src/main/kotlin/com/pokit/auth/port/out/GoogleApiClient.kt @@ -0,0 +1,7 @@ +package com.pokit.auth.port.out + +import com.pokit.user.dto.UserInfo + +interface GoogleApiClient { + fun getUserInfo(authorizationCode: String): UserInfo +} diff --git a/application/src/main/kotlin/com/pokit/auth/port/out/RefreshTokenPort.kt b/application/src/main/kotlin/com/pokit/auth/port/out/RefreshTokenPort.kt new file mode 100644 index 00000000..d932a508 --- /dev/null +++ b/application/src/main/kotlin/com/pokit/auth/port/out/RefreshTokenPort.kt @@ -0,0 +1,11 @@ +package com.pokit.auth.port.out + +import com.pokit.token.model.RefreshToken + +interface RefreshTokenPort { + fun persist(refreshToken: RefreshToken): RefreshToken + + fun loadByUserId(userId: Long): RefreshToken? + + fun deleteById(id: Long) +} diff --git a/application/src/main/kotlin/com/pokit/auth/port/out/RefreshTokenRepository.kt b/application/src/main/kotlin/com/pokit/auth/port/out/RefreshTokenRepository.kt deleted file mode 100644 index bbc0f459..00000000 --- a/application/src/main/kotlin/com/pokit/auth/port/out/RefreshTokenRepository.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.pokit.auth.port.out - -import com.pokit.token.model.RefreshToken - -interface RefreshTokenRepository { - fun save(refreshToken: RefreshToken): RefreshToken - - fun findByUserId(userId: Long): RefreshToken? - - fun deleteById(id: Long) -} diff --git a/application/src/main/kotlin/com/pokit/auth/port/service/AuthService.kt b/application/src/main/kotlin/com/pokit/auth/port/service/AuthService.kt new file mode 100644 index 00000000..b1541572 --- /dev/null +++ b/application/src/main/kotlin/com/pokit/auth/port/service/AuthService.kt @@ -0,0 +1,42 @@ +package com.pokit.auth.port.service + +import com.pokit.auth.port.`in`.AuthUseCase +import com.pokit.auth.port.`in`.TokenProvider +import com.pokit.auth.port.out.GoogleApiClient +import com.pokit.token.dto.request.SignInRequest +import com.pokit.token.model.AuthPlatform +import com.pokit.token.model.Token +import com.pokit.user.dto.UserInfo +import com.pokit.user.model.Role +import com.pokit.user.model.User +import com.pokit.user.port.out.UserPort +import org.springframework.stereotype.Service + +@Service +class AuthService( + private val googleApiClient: GoogleApiClient, + private val tokenProvider: TokenProvider, + private val userPort: UserPort, +) : AuthUseCase { + override fun signIn(request: SignInRequest): Token { + val platformType = AuthPlatform.of(request.authPlatform) + + val userInfo = + when (platformType) { + AuthPlatform.GOOGLE -> googleApiClient.getUserInfo(request.authorizationCode) + AuthPlatform.APPLE -> UserInfo("apple@apple.com") // TODO + } + + val userEmail = userInfo.email + val user = userPort.loadByEmail(userEmail) ?: createUser(userEmail) // 없으면 저장 + + val token = tokenProvider.createToken(user.id) + + return token + } + + private fun createUser(email: String): User { + val user = User(email = email, role = Role.USER) + return userPort.persist(user) + } +} diff --git a/application/src/main/kotlin/com/pokit/auth/port/service/JwtTokenProvider.kt b/application/src/main/kotlin/com/pokit/auth/port/service/JwtTokenProvider.kt index f82e7b21..b6917100 100644 --- a/application/src/main/kotlin/com/pokit/auth/port/service/JwtTokenProvider.kt +++ b/application/src/main/kotlin/com/pokit/auth/port/service/JwtTokenProvider.kt @@ -1,10 +1,10 @@ package com.pokit.auth.port.service -import com.pokit.auth.exception.AuthErrorCode import com.pokit.auth.port.`in`.TokenProvider -import com.pokit.auth.port.out.RefreshTokenRepository +import com.pokit.auth.port.out.RefreshTokenPort import com.pokit.auth.property.JwtProperty import com.pokit.common.exception.ClientValidationException +import com.pokit.token.exception.AuthErrorCode import com.pokit.token.model.RefreshToken import com.pokit.token.model.Token import io.jsonwebtoken.* @@ -17,25 +17,25 @@ import java.util.* @Component class JwtTokenProvider( private val jwtProperty: JwtProperty, - private val refreshTokenRepository: RefreshTokenRepository, + private val refreshTokenPort: RefreshTokenPort, ) : TokenProvider { override fun createToken(userId: Long): Token { val accessToken = generateToken(userId, jwtProperty.accessExpiryTime) val refreshToken = generateToken(userId, jwtProperty.refreshExpiryTime) - refreshTokenRepository.save(RefreshToken(userId, refreshToken)) + refreshTokenPort.persist(RefreshToken(userId, refreshToken)) return Token(accessToken, refreshToken) } override fun reissueToken(refreshToken: String): String { val userId = getUserId(refreshToken) - refreshTokenRepository.findByUserId(userId) + refreshTokenPort.loadByUserId(userId) ?: throw ClientValidationException(AuthErrorCode.NOT_FOUND_TOKEN) return generateToken(userId, jwtProperty.accessExpiryTime) } override fun deleteRefreshToken(refreshTokenId: Long) { - refreshTokenRepository.deleteById(refreshTokenId) + refreshTokenPort.deleteById(refreshTokenId) } override fun getUserId(token: String): Long { diff --git a/application/src/main/kotlin/com/pokit/user/port/out/UserRepository.kt b/application/src/main/kotlin/com/pokit/user/port/out/UserRepository.kt new file mode 100644 index 00000000..d9b2943d --- /dev/null +++ b/application/src/main/kotlin/com/pokit/user/port/out/UserRepository.kt @@ -0,0 +1,9 @@ +package com.pokit.user.port.out + +import com.pokit.user.model.User + +interface UserPort { + fun persist(user: User): User + + fun loadByEmail(email: String): User? +} diff --git a/application/src/test/kotlin/com/pokit/auth/port/service/AuthServiceTest.kt b/application/src/test/kotlin/com/pokit/auth/port/service/AuthServiceTest.kt new file mode 100644 index 00000000..211a8521 --- /dev/null +++ b/application/src/test/kotlin/com/pokit/auth/port/service/AuthServiceTest.kt @@ -0,0 +1,49 @@ +package com.pokit.auth.port.service + +import com.pokit.auth.AuthFixture +import com.pokit.auth.port.`in`.TokenProvider +import com.pokit.auth.port.out.GoogleApiClient +import com.pokit.common.exception.ClientValidationException +import com.pokit.user.UserFixture +import com.pokit.user.port.out.UserRepository +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.annotation.DisplayName +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk + +@DisplayName("[AuthService Test]") +class AuthServiceTest : BehaviorSpec({ + val googleApiClient = mockk() + val tokenProvider = mockk() + val userRepository = mockk() + val authService = AuthService(googleApiClient, tokenProvider, userRepository) + + Given("사용자가 로그인할 때") { + val request = AuthFixture.getGoogleSigniInRequest() + val invalidPlatformRequst = AuthFixture.getInvalidSignInRequest() // 플랫폼명 오타 요청 + val user = UserFixture.getUser() + val userInfo = UserFixture.getUserInfo() + val token = AuthFixture.getToken() + + every { googleApiClient.getUserInfo(request.authorizationCode) } returns userInfo + every { userRepository.findByEmail(userInfo.email) } returns user + every { tokenProvider.createToken(user.id) } returns token + + When("구글 플랫폼으로 올바른 인증코드로 요청하면") { + val resultToken = authService.signIn(request) + Then("토큰이 정상적으로 반환된다.") { + resultToken.accessToken shouldBe token.accessToken + resultToken.refreshToken shouldBe token.refreshToken + } + } + When("잘못된 플랫폼을 요청으로 보내면") { + Then("예외가 발생한다.") { + shouldThrow { + authService.signIn(invalidPlatformRequst) + } + } + } + } +}) diff --git a/application/src/main/kotlin/com/pokit/common/exception/ClientValidationException.kt b/domain/src/main/kotlin/com/pokit/common/exception/ClientValidationException.kt similarity index 100% rename from application/src/main/kotlin/com/pokit/common/exception/ClientValidationException.kt rename to domain/src/main/kotlin/com/pokit/common/exception/ClientValidationException.kt diff --git a/application/src/main/kotlin/com/pokit/common/exception/ErrorCode.kt b/domain/src/main/kotlin/com/pokit/common/exception/ErrorCode.kt similarity index 99% rename from application/src/main/kotlin/com/pokit/common/exception/ErrorCode.kt rename to domain/src/main/kotlin/com/pokit/common/exception/ErrorCode.kt index cb0e9305..f9b4b54c 100644 --- a/application/src/main/kotlin/com/pokit/common/exception/ErrorCode.kt +++ b/domain/src/main/kotlin/com/pokit/common/exception/ErrorCode.kt @@ -4,5 +4,4 @@ interface ErrorCode { val message: String val code: String - } diff --git a/application/src/main/kotlin/com/pokit/common/exception/NotFoundCustomException.kt b/domain/src/main/kotlin/com/pokit/common/exception/NotFoundCustomException.kt similarity index 100% rename from application/src/main/kotlin/com/pokit/common/exception/NotFoundCustomException.kt rename to domain/src/main/kotlin/com/pokit/common/exception/NotFoundCustomException.kt diff --git a/domain/src/main/kotlin/com/pokit/token/dto/request/SignInRequest.kt b/domain/src/main/kotlin/com/pokit/token/dto/request/SignInRequest.kt new file mode 100644 index 00000000..cb9269f4 --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/token/dto/request/SignInRequest.kt @@ -0,0 +1,6 @@ +package com.pokit.token.dto.request + +data class SignInRequest( + val authPlatform: String, + val authorizationCode: String, +) diff --git a/application/src/main/kotlin/com/pokit/auth/exception/AuthErrorCode.kt b/domain/src/main/kotlin/com/pokit/token/exception/AuthErrorCode.kt similarity index 84% rename from application/src/main/kotlin/com/pokit/auth/exception/AuthErrorCode.kt rename to domain/src/main/kotlin/com/pokit/token/exception/AuthErrorCode.kt index 407b98cf..cc0d127e 100644 --- a/application/src/main/kotlin/com/pokit/auth/exception/AuthErrorCode.kt +++ b/domain/src/main/kotlin/com/pokit/token/exception/AuthErrorCode.kt @@ -1,4 +1,4 @@ -package com.pokit.auth.exception +package com.pokit.token.exception import com.pokit.common.exception.ErrorCode @@ -12,4 +12,5 @@ enum class AuthErrorCode( UNSUPPORTED_TOKEN("지원하지 않는 형식의 토큰입니다.", "A_004"), WRONG_SIGNATURE("JWT 서명이 서버에 산정된 서명과 일치하지 않습니다.", "A_005"), TOKEN_REQUIRED("토큰이 비어있습니다.", "A_006"), + INVALID_PLATFORM("플랫폼 타입이 올바르지 않습니다.", "A_007"), } diff --git a/domain/src/main/kotlin/com/pokit/token/model/AuthPlatform.kt b/domain/src/main/kotlin/com/pokit/token/model/AuthPlatform.kt new file mode 100644 index 00000000..e47021a5 --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/token/model/AuthPlatform.kt @@ -0,0 +1,20 @@ +package com.pokit.token.model + +import com.pokit.common.exception.ClientValidationException +import com.pokit.token.exception.AuthErrorCode + +enum class AuthPlatform( + val platform: String, +) { + GOOGLE("구글"), + APPLE("애플"), + ; + + companion object { + fun of(input: String): AuthPlatform { + return entries + .firstOrNull { it.platform == input } + ?: throw ClientValidationException(AuthErrorCode.INVALID_PLATFORM) + } + } +} diff --git a/domain/src/main/kotlin/com/pokit/user/dto/UserInfo.kt b/domain/src/main/kotlin/com/pokit/user/dto/UserInfo.kt new file mode 100644 index 00000000..519537ea --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/user/dto/UserInfo.kt @@ -0,0 +1,5 @@ +package com.pokit.user.dto + +data class UserInfo( + val email: String, +) diff --git a/domain/src/main/kotlin/com/pokit/user/exception/UserErrorCode.kt b/domain/src/main/kotlin/com/pokit/user/exception/UserErrorCode.kt new file mode 100644 index 00000000..84b9f860 --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/user/exception/UserErrorCode.kt @@ -0,0 +1,10 @@ +package com.pokit.user.exception + +import com.pokit.common.exception.ErrorCode + +enum class UserErrorCode( + override val message: String, + override val code: String, +) : ErrorCode { + INVALID_EMAIL("올바르지 않은 이메일 형식의 유저입니다.", "U_001"), +} diff --git a/domain/src/main/kotlin/com/pokit/user/model/Role.kt b/domain/src/main/kotlin/com/pokit/user/model/Role.kt new file mode 100644 index 00000000..f49ee63f --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/user/model/Role.kt @@ -0,0 +1,7 @@ +package com.pokit.user.model + +enum class Role( + val description: String, +) { + USER("ROLE_USER"), +} diff --git a/domain/src/main/kotlin/com/pokit/user/model/User.kt b/domain/src/main/kotlin/com/pokit/user/model/User.kt new file mode 100644 index 00000000..cf5811c0 --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/user/model/User.kt @@ -0,0 +1,19 @@ +package com.pokit.user.model + +import com.pokit.common.exception.ClientValidationException +import com.pokit.user.exception.UserErrorCode +import java.util.regex.Pattern + +data class User( + val id: Long = 0L, + val email: String, + val role: Role, +) { + init { + val pattern = + Pattern.compile( + "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$", + ) + if (!pattern.matcher(this.email).matches()) throw ClientValidationException(UserErrorCode.INVALID_EMAIL) + } +} diff --git a/entry/web/build.gradle.kts b/entry/web/build.gradle.kts index 5202a1bf..a2623e8b 100644 --- a/entry/web/build.gradle.kts +++ b/entry/web/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { implementation(project(":adapters:in-web")) implementation(project(":adapters:out-persistence")) implementation(project(":domain")) + implementation(project(":adapters:out-web")) // 라이브러리 implementation("org.springframework.boot:spring-boot-starter-web") diff --git a/entry/web/src/main/kotlin/com/pokit/WebApplication.kt b/entry/web/src/main/kotlin/com/pokit/WebApplication.kt index 54d0650a..22470873 100644 --- a/entry/web/src/main/kotlin/com/pokit/WebApplication.kt +++ b/entry/web/src/main/kotlin/com/pokit/WebApplication.kt @@ -9,5 +9,6 @@ import org.springframework.boot.runApplication class WebApplication fun main(args: Array) { + System.setProperty("spring.config.name", "application-out-web, application-core, application-out-persistence") runApplication(*args) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7744259f..ae386edb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,3 +7,4 @@ include("adapters") include("adapters:in-web") include("adapters:out-persistence") include("adapters:out-cache") +include("adapters:out-web")