Skip to content

Commit

Permalink
Merge pull request #31 from key-del-jeeinho/develop
Browse files Browse the repository at this point in the history
1.0.0-RELEASE
  • Loading branch information
key-del-jeeinho authored Aug 30, 2022
2 parents 0caef46 + b03fcf3 commit 9eedc21
Show file tree
Hide file tree
Showing 150 changed files with 4,384 additions and 3 deletions.
20 changes: 18 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
jacoco
id("org.springframework.boot") version "2.7.3"
id("io.spring.dependency-management") version "1.0.13.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
}

jacoco { toolVersion = "0.8.8" }

group = "gg.dak"
version = "0.0.1-SNAPSHOT"
version = "1.0.0-RELEASE2"
java.sourceCompatibility = JavaVersion.VERSION_17

configurations {
Expand All @@ -23,18 +26,31 @@ repositories {
}

dependencies {
//spring boot
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
//kotlin
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
//mysql
runtimeOnly("mysql:mysql-connector-java")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
//jwt
implementation ("io.jsonwebtoken:jjwt:0.9.1")
//swagger
implementation("io.springfox:springfox-swagger-ui:3.0.0")
implementation("io.springfox:springfox-boot-starter:3.0.0")
//test
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0")
testImplementation ("org.mockito:mockito-inline:4.7.0")
//cache
implementation ("org.springframework.boot:spring-boot-starter-cache")
}

tasks.withType<KotlinCompile> {
Expand Down
9 changes: 9 additions & 0 deletions src/main/kotlin/gg/dak/board_api/BoardApiApplication.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package gg.dak.board_api

import gg.dak.board_api.domain.account.config.JwtProperties
import gg.dak.board_api.domain.account.config.LoginProperties
import gg.dak.board_api.domain.post.config.PostProperties
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.web.servlet.config.annotation.EnableWebMvc

@SpringBootApplication
@EnableWebMvc
@EnableScheduling
@EnableConfigurationProperties(LoginProperties::class, JwtProperties::class, PostProperties::class)
class BoardApiApplication

fun main(args: Array<String>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package gg.dak.board_api.domain.account.advice

import gg.dak.board_api.domain.account.controller.AccountController
import gg.dak.board_api.domain.account.exception.UnknownUuidTokenException
import gg.dak.board_api.global.account.exception.UnknownAccountException
import gg.dak.board_api.global.error.data.response.ErrorResponse
import gg.dak.board_api.global.error.data.type.ErrorStatusType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice

@RestControllerAdvice(basePackageClasses = [AccountController::class])
class AccountAdvice {
@ExceptionHandler(UnknownUuidTokenException::class)
fun handle(e: UnknownUuidTokenException) =
ErrorResponse(
status = ErrorStatusType.UNKNOWN_TOKEN,
message = e.getErrorMessage(),
details = e.getErrorDetails()
).let { ResponseEntity.badRequest().body(it) }

@ExceptionHandler(UnknownAccountException::class)
fun handle(e: UnknownAccountException) =
ErrorResponse(
status = ErrorStatusType.UNKNOWN_ACCOUNT,
message = e.getErrorMessage(),
details = e.getErrorDetails()
).let { ResponseEntity.badRequest().body(it) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gg.dak.board_api.domain.account.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding

@ConstructorBinding
@ConfigurationProperties("jwt")
data class JwtProperties(
val secretKey: String,
val issuer: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gg.dak.board_api.domain.account.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding

@ConstructorBinding
@ConfigurationProperties(prefix = "account.login")
data class LoginProperties(
val refreshTokenProperties: RefreshTokenProperties,
val accessTokenProperties: AccessTokenProperties
) {
data class RefreshTokenProperties(val expireSecond: Long)
data class AccessTokenProperties(val expireSecond: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package gg.dak.board_api.domain.account.controller

import gg.dak.board_api.domain.account.data.request.LoginRequest
import gg.dak.board_api.domain.account.data.request.RefreshLoginRequest
import gg.dak.board_api.domain.account.data.request.RegisterRequest
import gg.dak.board_api.domain.account.data.response.LoginResponse
import gg.dak.board_api.domain.account.data.response.RegisterResponse
import gg.dak.board_api.domain.account.service.AccountService
import gg.dak.board_api.domain.account.service.RefreshLoginService
import gg.dak.board_api.domain.account.util.AccountConverter
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
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

@Api(tags = ["계정 API"])
@RestController
@RequestMapping("/api/v1/account")
class AccountController(
private val accountConverter: AccountConverter,
private val accountService: AccountService,
private val refreshLoginService: RefreshLoginService
) {
@ApiOperation(value = "회원가입", notes = "회원 초기정보를 통해 회원가입을 진행합니다.")
@PostMapping("/register") //회원가입 트랜잭션을 수행합니다.
fun register(@RequestBody request: RegisterRequest): ResponseEntity<RegisterResponse> =
accountConverter.toDto(request) //요청정보를 통해 Dto를 구성합니다.
.let { accountService.register(it) } //Dto를 통해 회원가입 로직을 수행하고, 가입된 계정정보를 반환합니다.
.let { RegisterResponse(it.idx) } //응답객체를 구성하여 반환합니다.
.let { ResponseEntity.ok(it) }

@ApiOperation(value = "로그인", notes = "회원 인증정보(아이디, 비밀번호)를 통해 로그인토큰을 발급합니다.")
@PostMapping("/login") //로그인 트랜잭션을 수행합니다.
fun login(@RequestBody request: LoginRequest): ResponseEntity<LoginResponse> =
accountConverter.toDto(request) //요청정보를 통해 Dto를 구성합니다.
.let { accountService.login(it) } //Dto를 통해 로그인 로직을 수행하고, 로그인 토큰을 반환합니다.
.let { accountConverter.toResponse(it) } //응답객체를 구성하여 반환합니다.
.let { ResponseEntity.ok(it) }


@ApiOperation(value = "로그인 연장", notes = "회원 인증 연장정보(재발급 토큰)를 통해 로그인토큰을 발급합니다..")
@PostMapping("/login/refresh") //로그인 연장 트랜잭션을 수행합니다.
fun refreshLogin(@RequestBody request: RefreshLoginRequest): ResponseEntity<LoginResponse> =
refreshLoginService.refreshLogin(request.refreshToken) //로그인 연장로직을 수행하고, 로그인 토큰을 반환합니다.
.let { accountConverter.toResponse(it) } //응답객체를 구성하여 반환합니다.
.let { ResponseEntity.ok(it) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package gg.dak.board_api.domain.account.controller

import gg.dak.board_api.domain.account.data.response.AccountQueryResponse
import gg.dak.board_api.domain.account.data.response.PageableAccountQueryResponse
import gg.dak.board_api.domain.account.service.AccountQueryService
import gg.dak.board_api.domain.account.util.AccountQueryConverter
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import org.springframework.data.domain.PageRequest
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@Api(tags = ["계정 조회 API"])
@RestController
@RequestMapping("/api/v1/account/query")
class AccountQueryController(
private val accountQueryService: AccountQueryService,
private val accountQueryConverter: AccountQueryConverter
) {
@ApiOperation(value = "전체 계정목록 조회(with Pagination)", notes = "페이지네이션된 전체 계정목록을 조회합니다.")
@GetMapping("/all")
fun findAllAccountWithPagination(
@RequestParam("page") page: Int,
@RequestParam("size") size: Int
): ResponseEntity<PageableAccountQueryResponse> =
accountQueryService.findAllAccount(PageRequest.of(page, size))
.map { accountQueryConverter.toResponse(it) }
.let { accountQueryConverter.toPageableResponse(it.toList()) }
.let { ResponseEntity.ok(it) }

@ApiOperation(value = "인덱스로 계정 조회", notes = "계정의 인덱스로 계정정보를 조회합니다.")
@GetMapping("/{idx}")
fun findAccountByIndex(@PathVariable idx: Long): ResponseEntity<AccountQueryResponse> =
accountQueryService.findAccountByIndex(idx)
.let { accountQueryConverter.toResponse(it) }
.let { ResponseEntity.ok(it) }

@ApiOperation(value = "아이디로 계정조회", notes = "계정의 아이디로 계정정보를 조회합니다.")
@GetMapping("/id/{id}")
fun findAccountById(@PathVariable id: String): ResponseEntity<AccountQueryResponse> =
accountQueryService.findAccountById(id)
.let { accountQueryConverter.toResponse(it) }
.let { ResponseEntity.ok(it) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package gg.dak.board_api.domain.account.data.dto

data class LoginTokenDto(val accessToken: String, val refreshToken: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package gg.dak.board_api.domain.account.data.enitty

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

@Entity
class Account (
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val idx: Long,
val nickName: String,
val id: String,
val encodedPassword: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gg.dak.board_api.domain.account.data.enitty

import org.springframework.data.annotation.Id
import org.springframework.data.redis.core.RedisHash
import org.springframework.data.redis.core.TimeToLive
import java.util.concurrent.TimeUnit

@RedisHash("refresh-token")
data class RefreshToken (
@Id val id: String,
val token: String,
@TimeToLive(unit = TimeUnit.SECONDS)
val timeToLive: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gg.dak.board_api.domain.account.data.enitty

import org.springframework.data.annotation.Id
import org.springframework.data.redis.core.RedisHash
import org.springframework.data.redis.core.TimeToLive
import java.util.concurrent.TimeUnit

@RedisHash("uuid-token")
data class UuidToken(
@Id val token: String,
val data: Map<String, String>,
@TimeToLive(unit = TimeUnit.SECONDS)
val timeToLive: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.dak.board_api.domain.account.data.event

data class LoginTokenCreateEvent(
val id: String,
val accessToken: String,
val refreshToken: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package gg.dak.board_api.domain.account.data.event

class RefreshTokenDeleteEvent(val id: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package gg.dak.board_api.domain.account.data.event

data class UuidTokenDeleteEvent(
val token: String,
val payload: Map<String, String>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package gg.dak.board_api.domain.account.data.request

data class LoginRequest(val id: String, val password: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package gg.dak.board_api.domain.account.data.request

data class RefreshLoginRequest(val refreshToken: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.dak.board_api.domain.account.data.request

data class RegisterRequest(
val nickname: String,
val id: String,
val password: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.dak.board_api.domain.account.data.response

data class AccountQueryResponse(
val idx: Long,
val nickname: String,
val id: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package gg.dak.board_api.domain.account.data.response

data class LoginResponse(val accessToken: String, val refreshToken: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gg.dak.board_api.domain.account.data.response

import org.springframework.data.domain.Page

data class PageableAccountQueryResponse(
val data: Page<AccountQueryResponse>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package gg.dak.board_api.domain.account.data.response

data class RegisterResponse(val accountIdx: Long)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package gg.dak.board_api.domain.account.data.type

enum class OperationType {
REGISTER,
LOGIN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package gg.dak.board_api.domain.account.data.type

enum class TokenType(val key: String) {
LOGIN_ACCESS("login-access"), LOGIN_REFRESH("login-refresh")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gg.dak.board_api.domain.account.exception

import gg.dak.board_api.global.error.exception.GlobalException

class UnknownUuidTokenException(private val errorMessage: String, val token: String) : RuntimeException("$errorMessage - $token"),
GlobalException {
override fun getErrorMessage() = errorMessage
override fun getErrorDetails() = "$token 은 존재하지않는 UUID토큰입니다."
}
Loading

0 comments on commit 9eedc21

Please sign in to comment.