-
Notifications
You must be signed in to change notification settings - Fork 0
마이크로서비스 아키텍처(MSA) 구현 #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| language: "ko" | ||
| early_access: false | ||
| reviews: | ||
| profile: "chill" | ||
| request_changes_workflow: false | ||
| high_level_summary: true | ||
| poem: true | ||
| review_status: true | ||
| collapse_walkthrough: false | ||
| auto_review: | ||
| enabled: true | ||
| drafts: false | ||
| path_filters: | ||
| - "!**/*.md" | ||
| - "!**/docs/**" | ||
| - "!**/.github/**" | ||
| path_instructions: | ||
| - path: "**/*.java" | ||
| instructions: | | ||
| Review this Java code for: | ||
| 1. Spring Boot best practices | ||
| 2. Clean code principles | ||
| 3. Performance optimizations | ||
| 4. Security considerations | ||
| 5. Suggest more elegant solutions using Java features | ||
| 6. Check for proper exception handling | ||
| 7. Suggest better naming conventions | ||
| - path: "**/build.gradle" | ||
| instructions: | | ||
| Review Gradle configuration for: | ||
| 1. Dependency management best practices | ||
| 2. Build optimization opportunities | ||
| 3. Plugin usage efficiency | ||
| chat: | ||
| auto_reply: true |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| plugins { | ||
| id 'org.springframework.boot' version '3.1.5' | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation 'org.springframework.cloud:spring-cloud-starter-gateway' | ||
| implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.example.gateway; | ||
|
|
||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| import org.springframework.cloud.netflix.eureka.EnableEurekaClient; | ||
|
|
||
| @SpringBootApplication | ||
| @EnableEurekaClient | ||
| public class ApiGatewayApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| SpringApplication.run(ApiGatewayApplication.class, args); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| spring: | ||
| application: | ||
| name: api-gateway | ||
| cloud: | ||
| gateway: | ||
| routes: | ||
| - id: user-service | ||
| uri: lb://user-service | ||
| predicates: | ||
| - Path=/api/users/** | ||
| - id: order-service | ||
| uri: lb://order-service | ||
| predicates: | ||
| - Path=/api/orders/** | ||
| discovery: | ||
| locator: | ||
| enabled: true | ||
| lower-case-service-id: true | ||
|
|
||
| server: | ||
| port: 8080 | ||
|
|
||
| eureka: | ||
| client: | ||
| service-url: | ||
| defaultZone: http://localhost:8761/eureka/ | ||
| instance: | ||
| prefer-ip-address: true | ||
|
|
||
| logging: | ||
| level: | ||
| org.springframework.cloud.gateway: DEBUG |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| plugins { | ||
| id 'java' | ||
| } | ||
|
|
||
| allprojects { | ||
| group = 'com.example' | ||
| version = '0.0.1-SNAPSHOT' | ||
|
|
||
| repositories { | ||
| mavenCentral() | ||
| } | ||
| } | ||
|
|
||
| subprojects { | ||
| apply plugin: 'java' | ||
| apply plugin: 'io.spring.dependency-management' | ||
|
|
||
| java { | ||
| sourceCompatibility = '17' | ||
| } | ||
|
|
||
| configurations { | ||
| compileOnly { | ||
| extendsFrom annotationProcessor | ||
| } | ||
| } | ||
|
|
||
| ext { | ||
| set('springCloudVersion', "2022.0.4") | ||
| } | ||
|
|
||
| // 공통 의존성 | ||
| dependencies { | ||
| compileOnly 'org.projectlombok:lombok' | ||
| annotationProcessor 'org.projectlombok:lombok' | ||
| testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
| } | ||
|
|
||
| dependencyManagement { | ||
| imports { | ||
| mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" | ||
| } | ||
| } | ||
|
|
||
| tasks.named('test') { | ||
| useJUnitPlatform() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| dependencies { | ||
| implementation 'org.springframework.boot:spring-boot-starter-data-jpa' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.example.common; | ||
|
|
||
| import jakarta.persistence.*; | ||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
| import java.time.LocalDateTime; | ||
|
|
||
| @MappedSuperclass | ||
| @Getter | ||
| @Setter | ||
| public abstract class BaseEntity { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
|
|
||
| @Column(nullable = false, updatable = false) | ||
| private LocalDateTime createdAt; | ||
|
|
||
| @Column(nullable = false) | ||
| private LocalDateTime updatedAt; | ||
|
|
||
| @PrePersist | ||
| protected void onCreate() { | ||
| createdAt = LocalDateTime.now(); | ||
| updatedAt = LocalDateTime.now(); | ||
| } | ||
|
|
||
| @PreUpdate | ||
| protected void onUpdate() { | ||
| updatedAt = LocalDateTime.now(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| # 🤔 개발 과정에서의 질문과 답변 | ||
|
|
||
| ## MSA 아키텍처 관련 | ||
|
|
||
| ### Q: Spring Cloud 없이도 MSA가 가능한데 왜 사용하나요? | ||
|
|
||
| **A:** Spring Cloud를 사용하는 이유는 다음과 같습니다: | ||
|
|
||
| 1. **서비스 디스커버리 자동화** | ||
| ```java | ||
| // Spring Cloud 없으면 | ||
| RestTemplate.getForObject("http://user-service-1:8081/api/users/1", User.class); // 하드코딩 | ||
|
|
||
| // Spring Cloud 있으면 | ||
| RestTemplate.getForObject("http://user-service/api/users/1", User.class); // 자동 발견 | ||
| ``` | ||
|
|
||
| 2. **로드밸런싱 자동화** | ||
| - 인스턴스 장애 시 자동 전환 | ||
| - 인스턴스 추가/제거 시 자동 감지 | ||
|
|
||
| 3. **개발 편의성** | ||
| ```java | ||
| @FeignClient(name = "user-service") // 간단한 서비스 간 통신 | ||
| interface UserClient { | ||
| @GetMapping("/api/users/{id}") | ||
| User getUser(@PathVariable Long id); | ||
| } | ||
| ``` | ||
|
|
||
| **결론**: 작은 MSA는 Spring Cloud 없어도 되지만, 서비스가 많아질수록 Spring Cloud의 자동화 기능이 큰 도움이 됩니다. | ||
|
|
||
| --- | ||
|
|
||
| ## Gradle 멀티모듈 관련 | ||
|
|
||
| ### Q: 멀티모듈에서 공통 설정은 어떻게 관리하나요? | ||
|
|
||
| **A:** `subprojects` 블록을 활용해서 공통 설정을 자동 적용합니다: | ||
|
|
||
| ```groovy | ||
| subprojects { | ||
| // 모든 하위 모듈에 자동 적용 | ||
| dependencies { | ||
| compileOnly 'org.projectlombok:lombok' | ||
| testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| **장점**: | ||
| - 중복 설정 제거 | ||
| - 새 서비스 추가 시 최소한의 설정만 필요 | ||
| - 일관된 의존성 관리 | ||
|
|
||
| ### Q: 모든 서비스에서 공통 모듈을 꼭 사용해야 하나요? | ||
|
|
||
| **A:** 아닙니다! 필요한 모듈만 의존성을 추가하면 됩니다: | ||
|
|
||
| - **user-service**: `implementation project(':common')` ✅ (BaseEntity 사용) | ||
| - **order-service**: `implementation project(':common')` ✅ (BaseEntity 사용) | ||
| - **api-gateway**: 의존성 없음 ✅ (BaseEntity 안 씀) | ||
|
|
||
| **원칙**: 필요한 모듈만 의존성 추가, 불필요한 의존성은 추가하지 않음 | ||
|
|
||
| --- | ||
|
|
||
| ## FeignClient vs RestTemplate | ||
|
|
||
| ### Q: FeignClient를 왜 사용하나요? RestTemplate과 차이는? | ||
|
|
||
| **A:** 둘 다 MSA에서 서비스 간 통신에 사용되지만 편의성에 차이가 있습니다: | ||
|
|
||
| **RestTemplate (번거로움)**: | ||
| ```java | ||
| val response = restTemplate.getForObject("http://user-service/api/users/$id", UserDto::class.java) | ||
| ``` | ||
|
|
||
| **FeignClient (간편함)**: | ||
| ```java | ||
| @FeignClient(name = "user-service") | ||
| interface UserClient { | ||
| @GetMapping("/api/users/{id}") | ||
| UserDto getUserById(@PathVariable Long id); | ||
| } | ||
|
|
||
| // 사용 | ||
| val user = userClient.getUserById(id) | ||
| ``` | ||
|
|
||
| **결론**: RestTemplate도 가능하지만 FeignClient가 더 선언적이고 간편합니다. | ||
|
|
||
| --- | ||
|
|
||
| ## 기술 선택 이유 | ||
|
|
||
| ### Q: Maven 대신 Gradle을 선택한 이유는? | ||
|
|
||
| **A:** | ||
| - **빌드 속도**: Gradle이 더 빠름 | ||
| - **문법**: Groovy 문법이 XML보다 간결 | ||
| - **현대적**: 요즘 Spring 프로젝트에서 더 많이 사용 | ||
| - **유연성**: 복잡한 빌드 로직 구현 시 더 유연 | ||
|
|
||
| ### Q: Java 대신 Kotlin을 처음에 시도한 이유는? | ||
|
|
||
| **A:** Kotlin의 장점을 경험해보고 싶어서였지만, Java와 비교 학습을 위해 Java로 변경했습니다: | ||
|
|
||
| **Kotlin 장점**: | ||
| - data class로 boilerplate 코드 최소화 | ||
| - null safety | ||
| - 간결한 문법 | ||
|
|
||
| **Java를 최종 선택한 이유**: | ||
| - 두 언어의 차이점을 명확히 비교하기 위함 | ||
| - Lombok 적용 전후 비교 가능 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| plugins { | ||
| id 'org.springframework.boot' version '3.1.5' | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation project(':common') | ||
| implementation 'org.springframework.boot:spring-boot-starter-web' | ||
| implementation 'org.springframework.boot:spring-boot-starter-data-jpa' | ||
| implementation 'org.springframework.boot:spring-boot-starter-validation' | ||
| implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' | ||
| implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' | ||
|
|
||
| runtimeOnly 'com.h2database:h2' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.example.order; | ||
|
|
||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| import org.springframework.cloud.netflix.eureka.EnableEurekaClient; | ||
| import org.springframework.cloud.openfeign.EnableFeignClients; | ||
|
|
||
| @SpringBootApplication | ||
| @EnableEurekaClient | ||
| @EnableFeignClients | ||
| public class OrderServiceApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| SpringApplication.run(OrderServiceApplication.class, args); | ||
| } | ||
|
|
||
| } |
Uh oh!
There was an error while loading. Please reload this page.