안녕하세요. 하나셋입니다.
어....... 하하하
이 프로젝트는 사실 제가 카카오, 구글, 애플 등의 OAuth를 통해 유저를 관리하는 법을 좀 공부하기 위해서 만들었던 프로젝트입니다.
Spring, Jwt, Oauth를 검색하게 되면 되게... Spring Template 엔진을 사용한 설명들이 많아서.. 저는 솔직히..
프론트는 리엑트 또는 AOS/IOS를 사용하고 서버는 Spring을 사용하고자 하여.. 공부한 내용이기에
혹시 저와 같은 생각으로 Kakao Oauth를 통한 유저의 로그인 + Jwt를 통한 인증 + Security를 통한 인가를 찾고 계신다면
(인증: 유저가 누구인지 확인하는 절차) / (인가: 유저에 대한 권한을 허락 또는 확인하는 것)
큰 도움이 될거라고 생각이 듭니다.
아래에 레파지토리 주소를 적어놨으니 보시면서 이해하는 것도 좋을 거라 생각이 듭니다.
아 그리고 !! 이 프로젝트는 코틀린(Kotlin)을 기반으로 된 코드입니다.
그 부분 양해 부탁드립니다.
github.com/hanaset/Spring-Jwt-OAuth
설명은
1. 프로젝트 및 패키지 구조 설명
2. Spring Security 설명
3. Kakao OAuth 적용 설명
4. Jwt Token 설명
5. WebMvcConfigurationSupport 설명
순으로 진행하도록 하곘습니다.
1. 프로젝트 및 패키지 구조
이 Dobi 프로젝트는 멀티모듈로 구성되어 있습니다.
프로젝트를 멀티모듈로 구성하냐? 단일모듈로 구성하냐? 에 대한 질문은 사실 코딩 스타일의 차이라고 생각합니다.
저는 주로 common은 entity와 repository, db config를 주로 나두는데 그 이유는 api 서버 말고도 다른 서버를 만들 경우 똑같은 db를 바라보게 된다면 같은 내용의 코드를 또 작성해야하기 때문에 그 부분이 저는 싫어서 멀티모듈을 선호하는 편입니다.
(이건 누가 맞고 틀렸다의 내용보다는 스타일의 차이니 이해해주세요 ㅎㅎ)
그렇다면 간단하게 멀티모듈을 구성하는 법을 알아볼까요?
처음에 프로젝트를 만들어줍니다.!!!
그 후 Project Structure에서 모듈을 추가해줍니다.
모듈을 추가 후에 Proejct의 build.gradle을 작성합니다.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath( "org.jetbrains.kotlin:kotlin-noarg:1.3.71")
}
}
plugins {
id("org.springframework.boot") version "2.3.4.RELEASE" apply false
id("io.spring.dependency-management") version "1.0.10.RELEASE"
kotlin("jvm") version "1.3.71"
kotlin("plugin.spring") version "1.3.71"
kotlin("plugin.jpa") version "1.3.71"
}
allprojects {
group = "com.rufee.dobi"
version = "staging"
tasks.withType<JavaCompile> {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
}
subprojects {
repositories {
mavenCentral()
}
apply {
plugin("kotlin")
plugin("kotlin-spring")
plugin("kotlin-jpa")
plugin("idea")
plugin("eclipse")
plugin("org.springframework.boot")
plugin("io.spring.dependency-management")
plugin( "kotlin-allopen")
}
}
allOpen {
annotation("javax.persistence.Entity")
}
그 후 common의 build.gradle 작성
tasks.getByName<org.springframework.boot.gradle.tasks.bundling.BootJar>("bootJar") {
enabled = false
}
tasks.getByName<Jar>("jar") {
enabled = true
baseName = "dobi-common"
}
dependencies {
api(kotlin("reflect"))
api(kotlin("stdlib-jdk8"))
api("org.springframework.boot:spring-boot-starter")
api("org.springframework.boot:spring-boot-starter-data-jpa")
api("org.springframework.boot:spring-boot-starter-web")
api("com.squareup.retrofit2:retrofit:2.6.0")
api("com.squareup.retrofit2:converter-gson:2.6.0")
api("io.reactivex.rxjava2:rxjava:2.2.19")
api("com.squareup.retrofit2:adapter-rxjava:2.8.1")
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6")
api("io.reactivex.rxjava2:rxkotlin:2.4.0")
api("com.google.guava:guava:29.0-jre") // google collect
api("com.google.code.gson:gson:2.8.5")
compileOnly("org.projectlombok:lombok")
runtimeOnly("mysql:mysql-connector-java")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
}
여기서 api와 implementation의 차이는 common을 추가하여 사용하는 다른 모듈에서도 추가한 repository를 사용할수 잇냐? 없냐?의 차이입니다.
다음은 api 모듈의 build.gradle입니다.
object DependencyVersions {
const val SWAGGER_VERSION = "2.9.2"
}
dependencies {
implementation(project(":dobi-common"))
//swagger
implementation("io.springfox:springfox-swagger2:${DependencyVersions.SWAGGER_VERSION}")
implementation("io.springfox:springfox-swagger-ui:${DependencyVersions.SWAGGER_VERSION}")
// cache
implementation("org.ehcache:ehcache:3.8.1")
implementation("org.springframework.boot:spring-boot-starter-cache:2.3.4.RELEASE")
implementation("javax.cache:cache-api:1.1.1")
implementation ("org.springframework.boot:spring-boot-starter-security")
// jsoup
implementation("org.jsoup:jsoup:1.13.1")
// JWT Token
implementation ("io.jsonwebtoken:jjwt:0.9.1")
// jackson
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.google.apis:google-api-services-people:v1-rev20201013-1.30.10")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
testImplementation ("org.springframework.security:spring-security-test")
}
springBoot.buildInfo { properties { } }
configurations {
val archivesBaseName = "dobi-api-staging"
}
위와 같은 식으로 common 모듈을 추가합니다.
그리고 또 해야할게 있는데 각 모듈들의 setting.gradle은 지워주셔야합니다. 프로젝트 setting.gradle만 나두구요.
그리고 프로젝트의 setting.gradle은
rootProject.name = "dobi"
include("dobi-common", "dobi-api")
위와 같이 작성해주시면 됩니다.!!!
그러면 멀티모듈 구성 끝!!!!!
그런데 그래도 빌드 에러가 뜬다????
그렇다면 사이드에 있는 gradle을 클릭하셔서 보셨을때 메인 프로젝트만 있는게 아니라 프로젝트 안에 있어야할 모듈들이 존재하는걸 확인 하실수 있을 겁니다. 그냥 -(빼기)로 지워주시면 됩니다.
위 구조처럼 되어야합니다.!!!!
멀티모듈의 대한 설명이 끝났으니 디렉토리 구조의 대해서 이야기 해봅시다.
common의 경우 위에서도 언급 했듯이 주로 entity, repository, db config를 작성해놓습니다.
(DB config에 대한 자세한 설명은 다음에 따른 글에서 작성하도록 하겠습니다)
그렇다면 api 모듈은 어떤식으로 디렉토리가 구성되어 있을까요?
디렉토리(패키지)의 구조에는 2가지 방식이 존재하는데
1. 도메인형 구조
2. 게층형 구조 입니다.
위 구조에 대한 자세한 설명은 아래에서 읽어주세요 ㅎㅎ
cheese10yun.github.io/spring-guide-directory/
여튼 사실 2가지 구조 다 스타일이라고 말할 수 있지만 신입개발자 또는 연차가 낮은 개발자의 경우 이미 회사에서 정해놓은 디렉토리 구조가 있기 때문에 그에 따라가는게 맞다고 생각이 듭니다. (물론 의견을 내서 더 옳은 방향으로 이끌면 좋지만 이미 회사 정책이 정해지면 저 한사람의 힘으로는 바꾸기 힘듭니다..
또한 저는 계층 안에 도메인을 넣어주면 된다고 생각하는 부분이 있어서.. (여튼 취향입니다.)
디렉토리들을 열어보면 위와같은 구조로 되어 있음을 확인 할 수 있습니다.
(위 구조는 실제 제가 실무에서 사용하고 있는 디렉토리 구조입니다.)
다음 글
2020/11/05 - [백엔드/Spring Boot] - Spring boot + JWT+ Kakao OAuth / 2편 (Security)
2020/11/05 - [백엔드/Spring Boot] - Spring boot + JWT + Kakao OAuth / 3편 (WebMvcConfig)
'백엔드 > Spring Boot' 카테고리의 다른 글
업비트 자동매매 서버 만들기 (1~3편) (2) | 2020.11.14 |
---|---|
Spring boot + JWT + Kakao OAuth / 3편 (WebMvcConfig) (0) | 2020.11.05 |
Spring boot + JWT+ Kakao OAuth / 2편 (Security) (0) | 2020.11.05 |
[Spring boot] @Controller 와 @RestController의 차이 (0) | 2019.10.11 |
댓글