[Spring Cloud] - 3. API ์๋ฒ๋ก ์ค์ ๊ฐ ๋ถ๋ฌ์ค๊ธฐ
์ง๋ ํฌ์คํธ์์๋ Configuration Server๋ฅผ ์ค์ ํ๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์์๋ดค์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ๋ก์ปฌ์ ์ ์ฅ๋์ด ์๋ ์ค์ ํ์ผ์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ด์๊ณ , ๋ ๋ฒ์จฐ ๋ฐฉ๋ฒ์ Github ๋ฑ์ ๋ ํฌ์งํฐ๋ฆฌ์ ์๋ ํ์ผ์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ด์์ต๋๋ค.
์ฐ๋ฆฌ๋ ์ด๋ฌํ ์๋ฒ๊ฐ ์ ๋๋ก ๊ตฌ์ถ๋์ด ์๋์ง ํ์ธํ๊ธฐ ์ํด์ REST API๋ฅผ ์ง์ ํธ์ถํ์ฌ ํด๋น ๊ฐ์ ๋ฐํํ๋ ๋ฐฉ๋ฒ์ผ๋ก ํ ์คํธ๋ฅผ ํ์๋๋ฐ์. ํ์ง๋ง ์ด๋ฌํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ , ์ด๋ป๊ฒ API ์๋ฒ๊ฐ ์ด๋ฌํ ๊ฐ์ ๋ฐ๋์ง์ ๋ํด์๋ ์๋ง ๊ถ๊ธํด ํ์ จ์ ๊ฒ์ด๋ผ ์๊ฐํฉ๋๋ค.
๊ทธ๋์ ์ด๋ฒ ํฌ์คํธ์์๋ ์ง์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ง๋ค๊ณ , ํด๋น ์๋ฒ์ ๋ง๋ ์ค์ ๊ฐ์ ๋ง๋ค์ด, ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋จผ์ API ์๋ฒ๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ์ฃ .
์๋ก์ด Spring Boot ํ๋ก์ ํธ ์์ฑ
์ฐ๋ฆฌ๋ ์ง๋ ์๊ฐ์ ์ฌ์ฉํ Member, Note ์ด๋ ๊ฒ ๋ ๊ฐ์ง ์๋น์ค๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. Micro Service Architecture๋ก ์ค๊ณํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ ๊ฐ์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๋ฉด ๋๊ฒ ์ต๋๋ค.
์ ๋ ์์กด์ฑ ๋งค๋์ ๋ก Gradle์ ์ฃผ๋ก ์ฌ์ฉํฉ๋๋ค. ๋ฐ๋ผ์ Type์ Gradle๋ก ๋ง์ถฐ์ฃผ์๊ณ , ์ด๋ฒ ํฌ์คํธ์์๋ Java๊ฐ ์๋ Kotlin์ ์ฌ์ฉํ์ฌ API ์๋ฒ๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.2.2.RELEASE"
id("io.spring.dependency-management") version "1.0.8.RELEASE"
kotlin("jvm") version "1.3.61"
kotlin("plugin.spring") version "1.3.61"
}
group = "xyz.neonkid"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
}
extra["springCloudVersion"] = "Hoxton.SR1"
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-rest")
implementation("org.springframework.cloud:spring-cloud-starter-config")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
}
dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
Member API, Note API ๋ชจ๋ ๋์ผํ Gradle ํ์ผ๋ก ๋ง๋ค์ด์ค๋๋ค.
Member API
ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์์ผ๋ ์ด์ ์๋ฒ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋๋ก ํ์ฃ . ํฌ์คํธ์์๋ ์ ํํ ๋ก์ง์ ๊ตฌํํ๊ธฐ ๋ณด๋ค๋ Configuration Server์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋์ง ํ์ ํ๊ธฐ ์ํด, Configuration Server์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ๋ก์ง์ ๊ตฌํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
package xyz.neonkid.cloudmember
import org.springframework.beans.factory.annotation.Value
import org.springframework.cloud.context.config.annotation.RefreshScope
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
/**
* Created by neonkid on 12/29/19
*/
@RestController
@RequestMapping(value = ["v1"])
@RefreshScope
class MemberController {
@Value(value = "\${server.port}") lateinit var port: String
@Value(value = "\${spring.message}") lateinit var message: String
@GetMapping(value = ["/member/detail"])
fun message() = "Member API Info - Port $port - $message"
}
๊ฐ๋จํ ํ์ฌ ์๋ฒ์ ํฌํธ๋ฒํธ์ ๊ทธ๋ฆฌ๊ณ , Configuration Server์์ ๊ฐ ์ค์ ๋ณ๋ก ์ ํ์๋ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ผ๋ก ๋ง๋ค์ด๋ดค์ต๋๋ค.
@RefreshScope
์ด๋ ธํ ์ด์ ์ Spring Cloud Config Server์์ ์ค์ ๊ฐ์ ์๋ก๊ณ ์นจ ํ๋๋กํ๋ ์ด๋ ธํ ์ด์ ์ ๋๋ค. ๋ณดํต ๋ก์ปฌ ํ๊ฒฝ์์ ์ค์ ๊ฐ์ ๊ฐ์ง๊ฒ ๋ ๊ฒฝ์ฐ, ์๋ฒ๊ฐ ์์๋ ๋ ํ ๋ฒ ๋ก๋๋ ํ, ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌ๋์ด ๋์ํ๊ธฐ ๋๋ฌธ์ ์๋ฒ๊ฐ ์ฌ์์๋์ง ์๊ณ ์๋ ๋ณ๊ฒฝ๊ฐ์ด ์ ์ฉํ์ง ์์ง๋ง, ์ด ์ด๋ ธํ ์ด์ ์ ์๋ฒ์์ ์ค์ ๊ฐ์ด ๋ฐ๋๋ฉด ์ค๋จ์์ด ๋ค์ ๋ก๋ํ ์ ์๋๋ก ํ๋ ์ด๋ ธํ ์ด์ ์ด๋ผ๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
# bootstrap.yml
server:
port: 8000
spring:
profiles:
active: dev
application:
name: member-service
management:
endpoints:
web:
exposure:
include: info, refresh
# bootstrap-dev.yml
spring:
profiles: dev
cloud:
config:
uri: http://localhost:9000
fail-fast: true
Configuration Server์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ค์ ํ์ผ์ ์ด๋ฆ์ bootstrap-*.yml๋ก ํฉ๋๋ค. bootstrap.yml์๋ ์ด๋ค ํ๋กํ์ผ์ ์ค์ ๊ฐ์ ๋ถ๋ฌ์ฌ ๊ฒ์ธ์ง ์ ํ๋ฉด ๋๊ณ , bootstrap-*์ ํด๋น ํ๋กํ์ผ์ ์ด๋ฆ๊ณผ ํจ๊ป Configuration Server์ URI๋ฅผ ์ ์ด์ฃผ๋ฉด ์์ฑ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ , managment.endpoints.web.exposure.include ์ค์ ์ ์ฃผ์๋๋ฐ, ์ด ์ต์ ์ refresh API๋ฅผ ํ์ฑํ ์ํด์ผ๋ก์จ ์๋ฒ๊ฐ ์ค๋จ๋์ง ์์ ์ํ์์๋ Configuration Server์ ์ค์ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ API๋ฅผ ํ์ฑํ ์์ผ์ฃผ๋ ์ต์ ์ ๋๋ค.
Note API
Note API๋ Member API์ ๋์ผํ๊ฒ ๋ง๋ค์ด์ฃผ๋ฉด ๋ฉ๋๋ค.
package xyz.neonkid.cloudnote
import org.springframework.beans.factory.annotation.Value
import org.springframework.cloud.context.config.annotation.RefreshScope
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
/**
* Created by neonkid on 12/29/19
*/
@RestController
@RequestMapping(value = ["v1"])
@RefreshScope
class NoteController {
@Value(value = "\${server.port}") lateinit var port: String
@Value(value = "\${spring.message}") lateinit var message: String
@GetMapping(value = ["note/detail"])
fun message() = "Note API Info - Port $port - $message"
}
Kotlin ์ฝ๋๋ฅผ ์์ฑํ๋ค ์๊พธ ์ธ๋ฏธ์ฝ๋ก ๋๋ฅด๋ ์ต๊ด์ด.... ;;
# bootstrap.yml
server:
port: 8001
spring:
profiles:
active: prod
application:
name: note-service
management:
endpoints:
web:
exposure:
include: info, refresh
# bootstrap-prod.yml
spring:
profiles: prod
cloud:
config:
uri: http://localhost:9000
fail-fast: true
๊ฐ ์๋น์ค๋ณ๋ก ํฌํธ ๋ฒํธ๋ฅผ ๋ค๋ฅด๊ฒ ์ฃผ์ด์ผ๊ฒ ์ฃ ? ๊ทธ๋ ์ง ์์ผ๋ฉด.... ํฌํธ ์ถฉ๋์ด ์ผ์ด๋๋ ์ฃผ์.
Run Server
๋ชจ๋ ์๋ฒ๊ฐ ๋ง๋ค์ด์ก๋ค๋ฉด ์ด์ ์๋ฒ๋ฅผ ์คํํด๋ณด๋๋ก ํ์ฃ . ๋จผ์ Configuration Server๋ฅผ ์คํํ๊ณ ๋ค์ ๊ฐ API ์๋ฒ๋ฅผ ์คํํ๋ฉด ๋๊ฒ ์ต๋๋ค.
2019-12-29 12:59:17.992 INFO 21594 --- [nio-9000-exec-1] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:///home/neonkid/server-configs/member-service-dev.yml
2019-12-29 12:59:20.609 INFO 21594 --- [nio-9000-exec-2] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:///home/neonkid/server-configs/member-service-dev.yml
2019-12-29 13:16:19.169 INFO 21594 --- [nio-9000-exec-4] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:///home/neonkid/server-configs/note-service-prod.yml
2019-12-29 13:16:21.786 INFO 21594 --- [nio-9000-exec-5] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:///home/neonkid/server-configs/note-service-prod.yml
์์๋๋ก ์๋ฒ๋ฅผ ์คํํ๋ค๋ฉด, Configuration Server์์ ์์ ๊ฐ์ ๋ฉ์์ง๊ฐ ๋ํ๋ ๊ฒ์ ๋๋ค.
$ curl http://localhost:8000/v1/member/detail
$ curl http://localhost:8001/v1/note/detail
์ API๋ฅผ ๊ฐ๊ฐ ๋ถ๋ฅด๊ฒ ๋๋ฉด, Member API๋ ๊ฐ๋ฐ ์๋ฒ์ ์ค์ ๊ฐ์, Note API๋ ๋ฐฐํฌ ์๋ฒ์ ์ค์ ๊ฐ์ ๊ฐ์ ธ์ค๊ฒ ๋ฉ๋๋ค.
Member API Info - Port 8000 - Loaded MemberService for Development Mode..
Note API Info - Port 8001 - Loaded NoteService for Production Mode..
Refresh Config
๋ง์ง๋ง์ผ๋ก ์ค์ ๊ฐ์ ์๋น์ค์ ์ค๋จ์์ด ๋๊ธฐํ ํ๋ ๊ฒ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ด ๊ณผ์ ์ ์ค์ง ์ค์ ๊ฐ์ ๋ํ ์ถ๊ฐ ํน์ ๋ณ๊ฒฝ์ ๋ํ ๊ฒ์ด ์ ์ฉ๋ฉ๋๋ค. ๋ง์ฝ, ๊ฐ๋ฐ ์๋ฒ์ ์ค์ ํ๋กํ์ผ์ ๋ฐฐํฌ ์๋ฒ์ ์ค์ ํ๋กํ์ผ๋ก ๋ฐ๊พธ๋ ๋ฑ์ ๋ฐฉ๋ฒ์ ํ์ฉ๋์ง ์์ผ๋ ์ด ๋ถ๋ถ์ ์ฝ๊ธฐ ์ ์ ๋ฐ๋์ ์ฐธ๊ณ ๋ฐ๋๋๋ค.
์ฐ๋ฆฌ๋ ์ฌ๊ธฐ์์ spring.message ๋ผ๋ ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํ์์ต๋๋ค. ๋ฐ๋ผ์ ์ด ๊ฐ์ ๋ณ๊ฒฝํ ๋ค, ๊ฐ API ์๋ฒ๋ณ๋ก /actuator/refresh ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด, ์๋น์ค์ ์ค๋จ์์ด ๋ฐ๋ก ์ค์ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
spring:
profiles: dev
message: This configuration is development for member API..
๊ฐ๋จํ ์์๋ก Member API์ ๊ฐ๋ฐ ์๋ฒ ์ค์ ํ์ผ์ ๋ฉ์์ง๋ฅผ ์์ ๊ฐ์ด ๋ณ๊ฒฝํด๋ณด๊ฒ ์ต๋๋ค.
Refresh API๋ฅผ ํธ์ถํ๊ธฐ ์ ๊น์ง๋ ๋ณ๊ฒฝ ์ฌํญ์ด ์ ์ฉ๋์ง ์๊ณ , ๋ฉ์๋๋ฅผ ๋ถ๋ฅธ ๋ค์๋ ๋ณ๊ฒฝ๋ Property ์ด๋ฆ์ ๋ฐฐ์ด ํํ๋ก ์ถ๋ ฅํด์ฃผ๋ฉฐ, ๋ค์ ํธ์ถ์์๋ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ๋ฐ์ํด์ฃผ๋ ๋ชจ์ต์ ๋๋ค.
๋ง์น๋ฉฐ...
Configuration Server ๊ตฌ์ฑ ์ดํ, API ์๋น์ค์ ์ค์ ํ์ผ์ ๋๊ธฐํ ํ๋ ๊ฒ๊น์ง ์์๋ดค์ต๋๋ค. ๋ด์ฉ ์์ฒด๋ ๋ณ ๋ง์ ๋ด์ฉ์ด ์๋ ๊ฒ์ ์๋๋๋ค. ๋ค๋ง ์ฌ๋ฌ๊ฐ์ง ์ฌ์ฉ ๋ฐฉ๋ฒ์ด ์กด์ฌํ๊ณ , ๊ทธ๊ฒ์ ์์ ์ ์๋น์ค์ ๋ง๊ฒ ๊ฐ๋ฐํ๋ ๊ฒ์ด ์ด ํฌ์คํธ์ ๋ชฉํ์ ๋๋ค.
์ด ๊ธ์ ์์ฑํ๋ฉด์ ๋ง์ดํฌ๋ก ์๋น์ค ์ํคํ ์ฒ ํํ์ ๊ฐ๋ฐ์ ๋ฏธ๋ฆฌ ํ ๊ฐ์ง API๋ฅผ ๋ง๋ค์ด๋๊ณ ํ ์คํธ ํด ๋ณผ ์ ์๋ค๋ ์ ์ด ๋ฉ๋ฆฌํธ๋ผ๊ณ ์๊ฐํ์ต๋๋ค. ๋ฌผ๋ก ์ด์ ๋ ๋ฒจ๊น์ง ๋์ด๊ฐ๋ค๋ฉด ๋ก๋ ๋ฐธ๋ฐ์ฑ ๋ฑ ๋ค์ํ ์๋ฒ ์ฌ์ด๋, ํด๋ผ์ด์ธํธ ์ฌ์ด๋์ ๊น์ ๋ฐฑ์๋๋ฅผ ๊ณ ๋ คํด์ผ ํ๋ ๋จธ๋ฆฌ ์ํ ์ ์ ๋น์ฐํ ์กด์ฌํ๊ฒ ์ง๋ง ๋ก๋ ๋ฐธ๋ฐ์ฑ์ด๋ ๋ฆฌ๋ฒ์ค ํ๋ก์์ ๊ฐ์ ๊ฒฝ์ฐ๋ ๋ชจ๋๋ฆฌํฑ ์ํคํ ์ฒ ๋ํ ๊ฑฐ์ณ์ผํ๋ ๋ฌธ์ ์ด๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ง์ดํฌ๋ก ์๋น์ค ์ํคํ ์ฒ๋ง์ด ๊ฐ์ง ๋จ์ ์ด๋ผ๊ณ ๋ณผ ์๋ ์์ ๊ฒ ๊ฐ๋ค์.
๋ค์ ํํธ์์๋ Gateway์ ๋ํ ์ด์ผ๊ธฐ๋ก ๋ง๋๋ต๋๋ก ํ๊ฒ ์ต๋๋ค.
'Programming > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring Cloud] - 5. Zuul Gateway๋ฅผ ์ด์ฉํ Filtering (0) | 2020.01.22 |
---|---|
[Spring Cloud] - 4. Zuul Gateway๋ฅผ ์ด์ฉํ Routing (0) | 2020.01.17 |
[Spring Cloud] - 2. Github and Configuration Server (0) | 2019.12.26 |
[Spring Cloud] - 1. Configuration Server ๊ตฌ์ฑ (0) | 2019.12.22 |
[GP] Spring boot์์ REST API ๊ฐ๋ฐ ์์ํด๋ณด๊ธฐ (0) | 2019.11.08 |