Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/cd-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: gateway-service dev CD 파이프라인

on:
workflow_run:
workflows: ["gateway-service CI pipeline"]
types:
- completed

jobs:
deploy:
runs-on: ubuntu-latest
environment: dev
permissions:
contents: read

steps:
- name: Docker 이미지 dev 서버 배포
uses: appleboy/ssh-action@master
with:
host: ${{secrets.DEV_HOST}}
username: ${{secrets.DEV_USERNAME}}
key: ${{secrets.DEV_KEY}}
script: |
cd /home/ubuntu
docker rm -f gateway-service-dev || true
docker compose pull gateway-service-dev
docker compose up -d --no-deps --force-recreate --pull always gateway-service-dev
docker image prune -f
28 changes: 28 additions & 0 deletions .github/workflows/cd-prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: gateway-service prod CD 파이프라인

on:
workflow_run:
workflows: ["gateway-service CI pipeline"]
types:
- completed

jobs:
deploy:
runs-on: ubuntu-latest
environment: dev
permissions:
contents: read

steps:
- name: Docker 이미지 dev 서버 배포
uses: appleboy/ssh-action@master
with:
host: ${{secrets.PROD_HOST}}
username: ${{secrets.PROD_USERNAME}}
key: ${{secrets.PROD_KEY}}
script: |
cd /home/ubuntu
docker rm -f gateway-service-prod || true
docker compose pull gateway-service-prod
docker compose up -d --no-deps --force-recreate --pull always gateway-service-prod
docker image prune -f
46 changes: 46 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: gateway-service CI pipeline

on:
push:
branches:
- dev
pull_request:
branches:
- dev
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
environment: production

steps:
- name: Checkout
uses: actions/checkout@v4

- name: jdk 설정
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'gradle'

- name: Gradle Wrapper 권한 부여
run: chmod +x gradlew

- name: gradle 빌드
run: ./gradlew clean build

- name: 도커 로그인
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}

- name: 이미지 빌드 및 푸시
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
tags: ${{ secrets.DOCKER_USERNAME }}/unionmate-gateway-service:latest
push: true
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM openjdk:21-jdk

COPY build/libs/*SNAPSHOT.jar app.jar

ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -Dspring.profiles.active=${PROFILE} -jar /app.jar"]
7 changes: 5 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ ext {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway-server-webmvc'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8'
}

dependencyManagement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication
public class GatewayServiceApplication {
Expand All @@ -10,4 +14,15 @@ public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}

@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
final WebClient.Builder builder = WebClient.builder();
return builder;
}

@Bean
public RestClient.Builder restClientBuilder() {
return RestClient.builder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.unionmate.gateway_service.global;

import java.util.Arrays;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class SecurityConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.setAllowCredentials(true);
config.setAllowedHeaders(List.of("*"));
config.setExposedHeaders(List.of("Authorization", "Authorization-refresh"));

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);

return new CorsWebFilter(source);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.unionmate.gateway_service.global.gateway;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpHeaders;

@Configuration
@Profile({"local","dev","prod"})
public class GatewayConfiguration {

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 인증 필요 없는 라우트
.route("backend_route", r -> r.path("/**")
.filters(f -> f
.removeRequestHeader(HttpHeaders.COOKIE)
)
.uri("lb://backend-service"))

//스웨거를 위한 라우트 설정(각 서비스마다 등록해줘야 합니다.)
.route("backend-service_api_docs", r -> r.path("/api-docs/backend/**")
.filters(f -> f
.rewritePath("/api-docs/backend/(?<rem>.*)", "/${rem}")
)
.uri("lb://backend-service"))
.build();
}
}
Comment on lines 10 to 33
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 config 설정에서 /**로 와일드 카드를 사용해주신 이유는 모든 요청(/**)을 backend-service로 전달해주는 역할을 한다고 이해하면 될까요 ??

.removeRequestHeader(HttpHeaders.COOKIE)로 클라이언트가 보낸 쿠키를 백엔드 서비스로 전달하지 않는다고 이해했는데, 이건 인증이 끝난 요청에 대한 내용에 대해서 무상태를 유지해주는 설정인지도 궁금했습니다

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

첫번째 질문에 대해서는 모든 요청(/**)을 backend-service로 전달해주는 역할이 맞습니다. 하지만 현재 저희는 user와 관련된 서비스, 메일과 관련된 서비스가 추가될 예정이기에 추후 해당 부분은 수정될 예정입니다. 예를 들면 다음 코드와 같이 구현될 예정입니다.

.route("user_public_route", r -> r.path("/users/public/**")
				.filters(f -> f
					.removeRequestHeader(HttpHeaders.COOKIE)
				)
				.uri("lb://USER-SERVICE"))

두번째 질문의 경우 취지는 동일합니다. .removeRequestHeader(HttpHeaders.COOKIE)의 경우 게이트웨이 -> 백엔드로 쿠키를 넘기지 않아 해당 서비스들이 쿠키나 세션 기반 상태에 의존하지 못하도록 합니다. 따라서 인증의 관점에서는 토큰만 보고 요청을 처리하는 상태를 강제합니다.

24 changes: 24 additions & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
server:
port: 8000

spring:
main:
web-application-type: reactive
application:
name: gateway-service

eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://3.34.87.16:8761/eureka

springdoc:
api-docs:
enabled: true
swagger-ui:
path: /swagger-ui.html
urls:
- name: backend-service
url: /api-docs/backend/v3/api-docs
24 changes: 24 additions & 0 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
server:
port: 8000

spring:
main:
web-application-type: reactive
application:
name: gateway-service

eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8761/eureka

springdoc:
api-docs:
enabled: true
swagger-ui:
path: /swagger-ui.html
urls:
- name: backend-service
url: /api-docs/backend/v3/api-docs
25 changes: 25 additions & 0 deletions src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
server:
port: 8000

spring:
main:
web-application-type: reactive
application:
name: gateway-service

eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://discovery-service-prod:8761/eureka

springdoc:
api-docs:
enabled: true
swagger-ui:
path: /swagger-ui.html
urls:
- name: backend-service
url: /api-docs/backend/v3/api-docs

1 change: 0 additions & 1 deletion src/main/resources/application.properties

This file was deleted.