빌드·배포·환경 설정

arch-devops config-build config-profiles config-secrets

이 노트가 답하는 질문

  • 신입이 노트북에서 air-intl-adapter를 처음 빌드하고 실행하려면 무엇을 어떤 순서로 해야 하나?
  • local, my, dev, qa, staging, prod 프로파일은 각각 무엇이 다른가?
  • 공급사 API 키 같은 비밀값은 어디서 오나? (스포일러: AWS Secrets Manager)
  • git push 하면 어떻게 운영(prod)까지 배포되나? (CI/CD 파이프라인)
  • TwaySEED.jar는 왜 libs/에 직접 들어있나?

관련: 런타임 설정·인프라 상세 · 빠른 참조표


1. 한눈에 보는 빌드·배포 스택

항목출처(file:line)
언어/JVMKotlin 2.0.0, Java 21 (Amazon Corretto)build.gradle.kts:16-17,21 / Dockerfile:1
빌드 도구Gradle 8.11 (Wrapper, Kotlin DSL)gradle/wrapper/gradle-wrapper.properties:3
프레임워크Spring Boot 3.3.0 + dependency-management 1.1.5build.gradle.kts:14-15
모듈 구조단일 모듈 (멀티모듈 아님)settings.gradle.kts:1
산출물(아티팩트)air-intl-adapter.jar (단일 fat jar)Dockerfile:30
런타임 베이스 이미지titicacadev/amazoncorretto:21-ddtrace-latestDockerfile:14
컨테이너 포트8080 (앱 자체는 8000 리슨)Dockerfile:32 / application.yml:6
비밀값 저장소AWS Secrets Manager (ap-northeast-2)application-*.yml, supplier/*.yml
캐시/세션Redis (Redisson, serverless ElastiCache)application-*.yml, redisson/*.yml
배포 대상AWS ECS (Fargate), ECR 이미지 푸시.github/workflows/cd.yml
관측Datadog APM(ddtrace), Sentry, Slack 경보Dockerfile:14-28, build.gradle.kts:64-79
CIGitHub Actions (PR마다 빌드).github/workflows/ci.yml

단일 모듈이다

도입부 맥락에서 “11개 공급사 모듈”이라 했지만 이건 Gradle 멀티모듈이 아니라 패키지 단위 모듈이다. settings.gradle.kts에는 rootProject.name 한 줄뿐이고 include(...)가 없다. 즉 11개 공급사는 전부 하나의 jar로 빌드되어 한 프로세스에서 함께 뜬다. 공급사 하나만 따로 배포/스케일링하는 건 구조상 불가능하다. (아키텍처의 “중앙 디스패처 없음”과 연결해서 이해할 것)


2. 신입 로컬 셋업: 순서대로 따라하기

flowchart TD
    S1["1. JDK 21 설치<br/>Corretto 또는 Temurin"]
    S2["2. 레포 클론<br/>git clone"]
    S3["3. AWS 자격증명<br/>aws sso login 또는 ~/.aws"]
    S4["4. ./gradlew build<br/>의존성 다운로드 + 컴파일 + 테스트"]
    S5["5. ./gradlew localBootRun<br/>프로파일 local,my 로 기동"]
    S6["6. Swagger UI 접속<br/>localhost 8000 swagger-ui.html<br/>API 문서 확인"]
    S1 --> S4
    S2 --> S4
    S3 --> S4
    S4 --> S5
    S5 --> S6

단계별 상세

단계 1~2: 사전 준비

# JDK 21 (Amazon Corretto 권장 — 운영 이미지와 동일 벤더)
java -version   # openjdk 21.x 확인
 
git clone <air-intl-adapter repo>
cd air-intl-adapter

Gradle은 별도 설치 불필요하다. Wrapper(./gradlew)가 gradle-wrapper.properties:3에 명시된 8.11을 자동으로 내려받는다.

단계 3: AWS 자격증명 (가장 잘 막히는 곳)

local/dev 프로파일은 부팅 시 optional:aws-secretsmanager:dev/air-intl-adapter를 읽는다(application-local.yml:5-7). 그래서 dev 계정에 접근 가능한 AWS 자격증명이 로컬 환경(~/.aws/credentials 또는 SSO)에 있어야 공급사 키가 채워진다.

  • 접두사 optional: 덕분에 Secrets Manager 호출이 실패해도 앱은 죽지 않는다. 다만 해당 비밀값에 의존하는 공급사 호출만 인증 실패한다.
  • 즉, AWS 없이도 “기동은 된다”. 실제 공급사 검색/예약을 테스트하려면 자격증명 필요.

단계 4: 빌드

./gradlew build              # 컴파일 + 테스트 + jar 생성
./gradlew --exclude-task test build   # 테스트 건너뛰고 빌드 (CI/Docker와 동일)

산출물: build/libs/air-intl-adapter.jar

단계 5: 실행

./gradlew localBootRun        # 권장: local,my 프로파일 자동 적용
# 또는 프로파일을 직접 지정
./gradlew bootRun --args="--spring.profiles.active=dev,my"

localBootRun은 커스텀 태스크다(build.gradle.kts:117-125). 내부적으로 bootRun--spring.profiles.active=local,my 인자를 붙여 실행한다.

단계 6: 테스트 / 문서

./gradlew test                                          # 전체 테스트
./gradlew test --tests "com...amadeus.application.AmadeusTest"  # 특정 테스트
# Swagger UI: local/dev/qa 에서만 활성(springdoc.api-docs.enabled=true)
#   → http://localhost:8000/swagger-ui.html

my 프로파일은 직접 만들어야 한다 (레포에 없음)

local,my 또는 dev,mymy개인 로컬 오버라이드 프로파일이다. .gitignore:38*-my.*가 있어서 application-my.yml은 git에 올라가지 않는다 → 레포 어디에도 없다. 본인 PC에 src/main/resources/application-my.yml을 직접 만들어 개인 설정(예: 특정 공급사 엔드포인트 모킹, 로컬 redis 주소 등)을 넣는 용도다. 파일이 없어도 동작한다 (Spring은 없는 프로파일 yml을 무시). 처음엔 만들 필요 없다.


3. build.gradle.kts 정밀 해부

3.1 버전 핀(extra 변수)

val sentryVersion: String by extra("6.30.0")        // 에러 추적
val awsVersion: String by extra("1.12.261")         // aws-java-sdk-s3
val resilience4jVersion: String by extra("2.2.0")   // 서킷브레이커/리트라이/벌크헤드
val redissonVersion: String by extra("3.39.0")      // Redis 클라이언트
val opentracingVersion: String by extra("0.33.0")   // Datadog 연동
val awsSecretsManager: String by extra("2.4.4")     // Secrets Manager 부트스트랩
val slackVersion: String by extra("1.44.2")         // Slack 경보

build.gradle.kts:5-11. 라이브러리 버전을 상단에 모아두는 관용 패턴. 나머지(jackson, kotlin-coroutines 등)는 Spring Boot BOM이 버전을 관리한다.

3.2 의존성 하이라이트 (왜 들어있나)

의존성좌표이 시스템에서의 역할
spring-boot-starter-web-services(BOM)SOAP 공급사(Amadeus/Sabre/Galileo)의 WebServiceTemplate
wss4j / wss4j-ws-security-dom1.6.19 / 2.4.1SOAP WS-Security 서명/암호화 (GDS 인증 헤더)
jackson-dataformat-xml(BOM)XML 페이로드(REST/NDC/SOAP) 직렬화
kotlinx-coroutines-core/slf4j(BOM)비동기 처리 (async-coroutines). slf4j 확장은 코루틴 간 MDC 전파
redisson-spring-boot-starter3.39.0Redis 캐시/세션. jedis·lettuce는 exclude (build.gradle.kts:37-38)
resilience4j-spring-boot3 / -all2.2.0서킷브레이커·리트라이·벌크헤드 (resilience-and-events)
spring-cloud-starter-aws-secrets-manager-config2.4.4부팅 시 Secrets Manager → 프로퍼티 주입
aws-java-sdk-s31.12.261S3 (인증서/리소스 파일 등)
sentry-spring-boot-starter-jakarta6.30.0예외를 Sentry로 전송
dd-trace-api / opentracing-* / logstash-logback-encoder-Datadog APM + JSON 구조화 로그
slack-api-client(+kotlin-extension)1.44.2상태 전파를 Slack 경보로
files(“libs/TwaySEED.jar”)로컬 jarT’way SEED 암호화 (4절 참조)
okhttp4.9.3일부 공급사 HTTP 클라이언트
springdoc-openapi-starter-webmvc-ui2.2.0Swagger UI
mockk1.11.0 (test)Kotlin 모킹

redisson이 jedis/lettuce를 exclude하는 이유

spring-boot-starter-data-redis는 기본 클라이언트로 Lettuce(또는 Jedis)를 끌고 온다. 이 프로젝트는 Redisson을 쓰므로 충돌·중복을 막으려 두 클라이언트를 명시적으로 배제한다(build.gradle.kts:37-38). Redis 설정은 redisson/*.yml로 따로 관리한다.

3.3 컴파일러 옵션 (주의 깊게 볼 것)

tasks.withType<KotlinJvmCompile>().configureEach {
    compilerOptions {
        freeCompilerArgs.add("-Xjsr305=strict")   // ★ JSR-305 null 어노테이션 엄격 모드
        jvmTarget.set(JvmTarget.JVM_21)
        languageVersion.set(KotlinVersion.KOTLIN_2_0)
    }
}

build.gradle.kts:104-111.

-Xjsr305=strict의 의미

Java 라이브러리(Spring 등)의 @Nullable/@NonNull 어노테이션을 Kotlin이 엄격하게 해석한다. 즉 Java API가 @NonNull을 선언했는데 null을 넘기면 컴파일 에러가 난다. NDC/SOAP의 방대한 자동생성 DTO를 다룰 때 이 옵션 때문에 플랫폼 타입이 더 까다로워진다. 컴파일 에러가 null 관련으로 났다면 이 옵션을 떠올릴 것.

3.4 기타

  • runtimeOnly netty-resolver-dns-native-macos:...:osx-aarch_64 (build.gradle.kts:98): Apple Silicon Mac 로컬 개발용 Netty DNS 네이티브 리졸버. 운영(리눅스)엔 영향 없음.
  • gradle.properties: kotlin.daemon.jvmargs=-Xmx2g — Kotlin 컴파일 데몬에 2GB 힙. 빌드가 OOM이면 이 값을 본다.
  • 버전 미선언 → 아티팩트가 air-intl-adapter.jar로 떨어짐. Spring Boot의 bootJar는 프로젝트 version이 없으면 ${rootProject.name}.jar를 만들고, Dockerfile:30이 정확히 이 이름을 복사한다. rootProject.name을 바꾸면 Dockerfile도 같이 고쳐야 한다.

4. libs/TwaySEED.jar — 벤더 동봉 jar

T’way Air는 카드정보 등 민감 필드를 SEED(한국 표준 블록암호)로 암호화해 전송해야 한다. T’way가 제공한 jar를 libs/에 직접 넣고 implementation(files("libs/TwaySEED.jar"))로 참조한다(build.gradle.kts:67).

jar 내용(zipfile로 확인한 클래스):

com/twayair/security/seed/SeedUtil.class      ← 코드에서 import 하는 진입점
com/twayair/security/seed/SEED_KISA.class     ← KISA SEED 알고리즘 구현
com/twayair/security/seed/Base64.class        ← 자체 Base64

사용처(코드에서 import com.twayair.security.seed.SeedUtil):

  • supplier/tway/infrastructure/TwayClient.kt:22
  • supplier/tway/infrastructure/ancillary/TwayAncillaryClient.kt:12
  • supplier/tway/infrastructure/ancillary/TwayAncillaryIssueTokenRequest.kt:3
  • supplier/tway/infrastructure/ancillary/TwayAncillaryDeepLink.kt:6
  • supplier/tway/infrastructure/request/CardInfo.kt:7

헷갈리지 말 것: SeedUtilSeedEncryptor

프로젝트에는 SEED 관련 코드가 두 갈래 있다.

클래스위치출처용도
com.twayair.security.seed.SeedUtillibs/TwaySEED.jar (벤더 동봉)T’way 제공T’way 트랜잭션 필드 암호화
support.util.SeedEncryptorsrc/.../support/util/SeedEncryptor.kt프로젝트 자체BouncyCastle SEED/CBC/PKCS5Padding 직접 구현

SeedEncryptor.kt:3org.bouncycastle.jce.provider.BouncyCastleProvider를 쓴다. 그런데 BouncyCastle은 build.gradle.kts에 직접 선언돼 있지 않다 → wss4j/xmlsec 등의 전이 의존성으로 클래스패스에 들어온다. 이 암묵적 의존은 landmines에 기록해 둘 만한 깨지기 쉬운 지점이다.

libs/TwaySEED.jar는 빌드 컨텍스트에 반드시 포함돼야 한다

Dockerfile:11COPY libs ./libs로 jar를 컨테이너 빌드 스테이지에 복사한다. 이 줄이 없거나 .dockerignore로 libs가 제외되면 컴파일 단계에서 com.twayair.security.seed 미해결로 빌드 실패한다. 또한 이 jar는 Maven Central에 없으므로 레포에서 삭제하면 어디서도 다시 받을 수 없다.


5. 프로파일 체계: 누가 무엇을 켜고 끄나

5.1 베이스 application.yml (모든 프로파일 공통)

application.yml이 항상 먼저 로드된다.

  • 포트 8000 (server.port, application.yml:6)
  • 10개 공급사 yml을 spring.config.import로 합침 (application.yml:10-20) — amadeus/sabre/singaporeair/tway/lufthansa/jinair/galileo/groupair/koreanair/jejuair
  • spring.threads.virtual.enabled: true가상 스레드(Java 21 Loom) 사용
  • ErrorMvcAutoConfiguration 제외 → 커스텀 에러 처리 (error-handling)
  • Resilience4j search 서킷브레이커 기본설정 + 11개 인스턴스 정의 (application.yml:37-70)
  • Slack 채널/대리점 전화번호 기본값

groupairSearch는 import 목록엔 없지만 서킷브레이커엔 있다

application.yml:10-20의 공급사 import는 amadeusndc를 포함하지 않지만, Resilience4j 인스턴스에는 amadeusndcSearchgroupairSearch가 모두 정의돼 있다(application.yml:51,65). amadeusndc 설정은 amadeus.yml에 합쳐져 있거나 코드에서 직접 다룬다는 신호. 설정 import 목록과 실제 활성 공급사 수(11)가 1:1로 안 맞을 수 있으니 주의.

5.2 프로파일별 차이 한눈 비교

항목localdevqastagingprod
logback 설정logback-local.xmllogback-dev.xmllogback-qa.xmllogback-staging.xmllogback-prod.xml
Secrets Managerdev/air-intl-adapterdev/air-intl-adapterqa/...staging/...prod/...
Redisson 파일redisson-local.ymlredisson-dev.ymlredisson-qa.ymlredisson-staging.ymlredisson-prod.yml
Swagger(api-docs)ononon(미설정→off)(미설정→off)
Slackactive: false(베이스값 true)(베이스)active: trueactive: true
Sentry env---stagingprod
city-api 엔드포인트dev proxy(https)dev(http)qastagingprod
supplier.logging.search전체 공급사전체 공급사전체 공급사(미설정)(미설정)

출처: application-local.yml, application-dev.yml, application-qa.yml, application-staging.yml, application-prod.yml.

local과 dev가 같은 비밀값을 공유한다

application-local.yml:7application-dev.yml:7 모두 dev/air-intl-adapter를 가리킨다. 즉 로컬 개발자는 dev 계정의 공급사 비밀값을 그대로 쓴다. local과 dev의 실질 차이는 (1) 로깅 설정, (2) Slack 비활성(local), (3) city-api가 local은 https proxy를 본다는 점 정도다.

5.3 비밀값 주입 흐름 (AWS Secrets Manager)

flowchart TD
    Boot["부팅 시 spring.config.import 가 순서대로 실행"]
    PYml["application-{profile}.yml"]
    SvcSecret["optional aws-secretsmanager env/air-intl-adapter<br/>서비스 공통 비밀"]
    BaseYml["application.yml 이 import 한 supplier/{name}.yml"]
    SupSecret["optional aws-secretsmanager env/air-intl-adapter/name<br/>공급사별 비밀"]
    Inject["Secrets Manager의 JSON 키들이<br/>Spring Environment 프로퍼티로 평탄화되어 주입"]
    Boot --> PYml
    PYml --> SvcSecret
    Boot --> BaseYml
    BaseYml --> SupSecret
    SvcSecret --> Inject
    SupSecret --> Inject
    Inject --> Bind["예: amadeus.officeId, tway.seedKey 등<br/>코드의 @Value 또는 @ConfigurationProperties 가 바인딩"]
  • 공급사 yml은 4개 문서(---)로 나뉘어 프로파일별 시크릿 경로를 분기한다. 예: supplier/amadeus.yml:1-31dev,local / qa / staging / prod 각각에 <env>/air-intl-adapter/amadeus를 import.
  • 전부 optional: 접두사 → Secrets Manager 미접속 시에도 부팅은 성공(해당 비밀만 비어있게 됨). configuration-and-infra에서 바인딩 대상 키를 더 다룬다.

인증서 파일은 git에 들어있다

Secrets Manager가 아니라 레포에 직접 있는 비밀성 파일도 있다: resources/supplier/jinair/aiRES_PYM_Cert.cer, payment.dev.cer. Jin Air 결제 인증서다. 민감 파일이 클래스패스로 패키징된다는 점을 인지할 것.

5.4 Redisson (Redis) 설정

redisson/redisson-<profile>.yml은 ElastiCache serverless 클러스터를 가리킨다.

  • prod: rediss://air-international-prod-serverless-ehfiks.serverless.apn2.cache.amazonaws.com:6379 (redisson-prod.yml:12)
  • local/dev: rediss://air-international-dev-ehfiks.serverless... (redisson-local.yml:12)
  • rediss://(TLS) + sslEnableEndpointIdentification: true. 커넥션 풀 master/slave 각각 min 24 / max 64.

로컬에서도 실제 dev Redis(ElastiCache)에 붙는다

redisson-local.yml은 로컬 Redis가 아니라 dev ElastiCache 클러스터를 본다. 따라서 로컬 기동 시 dev 네트워크(VPN/프록시) 접근 권한이 없으면 Redis 연결이 실패할 수 있다. 완전 오프라인 개발을 원하면 application-my.yml로 redisson 파일을 로컬 주소로 오버라이드해야 한다.


6. Docker 이미지 (멀티스테이지)

FROM amazoncorretto:21 AS base            # ① 빌드 컨텍스트 준비
COPY build.gradle.kts settings.gradle.kts gradlew gradlew.bat gradle.properties ./
COPY gradle ./gradle
 
FROM base AS build                        # ② 실제 빌드
COPY src ./src
COPY libs ./libs                          # ★ TwaySEED.jar 포함 (4절 참조)
RUN ./gradlew --exclude-task test build   # ★ 테스트 제외하고 빌드
 
FROM titicacadev/amazoncorretto:21-ddtrace-latest AS release   # ③ 런타임
ARG environment
ARG short_sha
ENV DD_ENV=${environment} DD_VERSION=${short_sha} DD_SERVICE=air-intl-adapter
# ... DD_* 추가 트레이싱 태그들 ...
COPY --from=build /app/build/libs/air-intl-adapter.jar .
EXPOSE 8080
CMD ["-jar", "air-intl-adapter.jar"]

Dockerfile:1-33.

세 가지 빌드 디테일

  1. 테스트 제외(--exclude-task test): 이미지 빌드 시 테스트를 안 돈다. 테스트는 CI(PR 단계)에서가 아니라… 사실 CI도 테스트를 제외한다(7절). → 테스트 회귀는 누군가 로컬에서 ./gradlew test를 돌려야만 잡힌다. landmines에 기록할 사항.
  2. 런타임 베이스가 ...-ddtrace-latest: Datadog 트레이싱 에이전트(-javaagent)가 베이스 이미지에 내장돼 있다. 그래서 CMDjava가 아니라 ["-jar", ...]로 시작한다 — 베이스 이미지의 ENTRYPOINT가 java -javaagent:dd-java-agent.jar로 추정되며 거기에 인자를 덧붙이는 구조다.
  3. 포트 불일치: EXPOSE 8080인데 앱은 server.port: 8000을 리슨(application.yml:6). EXPOSE는 문서용 메타데이터일 뿐 실제 바인딩과 무관하다. ECS task-definition/ALB target group이 8000을 가리켜야 트래픽이 통한다. 혼동 주의.
# 로컬 Docker 빌드 (CLAUDE.md 안내)
docker build -t air-intl-adapter .
# CI/CD와 동일하게 빌드 인자 주려면
docker build --build-arg environment=dev --build-arg short_sha=abc123 -t air-intl-adapter .

7. CI/CD 파이프라인 (GitHub Actions)

flowchart TD
    PR["PR 열림 또는 푸시"]
    CI["ci.yml<br/>./gradlew --exclude-task test build 검증만<br/>JDK21 + Gradle 캐시, 테스트는 안 돔"]
    Master["master 브랜치 푸시"]
    Tag["tag.yml<br/>release-qa 태그를 강제로 재생성/갱신"]
    Release["release-* 태그 푸시<br/>release-dev/qa/staging/prod*"]
    CD["cd.yml<br/>환경 판별, docker build, ECR push,<br/>ops API 호출로 ECS 배포,<br/>GitHub Deployment 상태 기록"]
    PR --> CI
    Master --> Tag
    Tag --> Release
    Release --> CD

7.1 ci.yml — PR 검증

  • 트리거: pull_request(opened/synchronize/reopened), 태그 push는 무시 (ci.yml:4-8)
  • JDK 21 셋업 + ~/.gradle/caches 캐시 + ./gradlew --exclude-task test build (ci.yml:34)
  • 테스트 미실행. (setup-node@v2/setup-java@v1 등 일부 액션 버전이 오래됨)

7.2 tag.yml — master → QA 자동 태깅

  • 트리거: master 브랜치 push (tag.yml:3-5)
  • release-qa 태그 객체를 만들고, 이미 있으면 force:true로 그 태그를 최신 커밋으로 이동(tag.yml:47-57)
  • 이 태그 갱신이 다시 cd.yml(아래)을 트리거 → master에 머지되면 자동으로 QA 배포되는 흐름

브랜치 이름이 master다

tag.yml:5master를 본다(현재 우리 환경의 git 기본 브랜치 main과 다름). air-intl-adapter 레포의 통합 브랜치는 master임을 기억할 것.

7.3 cd.yml — 태그 → 환경별 배포

  • 트리거: release-** 태그 push (브랜치 push는 전부 무시, cd.yml:3-7)
  • 태그 이름으로 환경/계정 분기 (cd.yml:48-75):
태그ENVIRONMENTTAG_SUFFIXAWS 계정tfstate 경로
release-prod*production-prod107243714588 (PROD)production/…/seoul.tfstate
release-qaqa-qa927056181394 (DEV)qa/…
release-stagingstaging-staging107243714588 (PROD)staging/…
release-devdev-dev107243714588 (PROD)development/…
  • 이미지 태그: <short_sha><suffix> (예: abc123-prod), ECR: <account>.dkr.ecr.ap-northeast-2.amazonaws.com/air-intl-adapter:<tag> (cd.yml:79-82)
  • 배포 실행: ops.inpk.io(Triple 내부 OPS API) v2 ecs-deploy-tfstate 엔드포인트에 POST → ECS 서비스가 새 이미지로 롤링 (cd.yml:101-107)
  • 성공/실패 시 GitHub Deployment 상태 갱신 (cd.yml:138-152)

qa만 DEV 계정, 나머지(dev/staging/prod)는 PROD 계정

cd.yml을 보면 환경 이름과 AWS 계정이 직관과 다르게 매핑돼 있다. release-devrelease-staging이 PROD 계정(107243714588)에 배포되고, release-qa만 DEV 계정(927056181394)에 배포된다(cd.yml:55-71). 배포 사고를 피하려면 “환경 이름 ≠ AWS 계정”임을 반드시 숙지할 것. landmines 핵심 항목.

scheduled-task 블록은 현재 비활성

cd.yml:110-136의 “Update scheduled-tasks”는 모든 환경에서 SCHEDULE_RULES=(빈 값)라 if [[ $SCHEDULE_RULES ]] 조건이 거짓 → 아무 것도 안 한다. 향후 배치/스케줄 태스크를 붙일 자리(placeholder)다.

7.4 거버넌스 파일

  • .github/CODEOWNERS: 전체(*)를 @eugene-triple @lucas-triple @hans-triple @triple-young이 소유 → 이들 중 1명 리뷰 승인 필요 (CODEOWNERS:1)
  • .github/PULL_REQUEST_TEMPLATE.md, ISSUE_TEMPLATE/*: PR/이슈 템플릿
  • .github/workflows/pr-auto-labeler.yml + pr-label.yml + labels.yml + labels.json: 브랜치명 기반 자동 라벨링

8. 자주 쓰는 명령 모음 (Quick Commands)

목적명령
전체 빌드(+테스트)./gradlew build
빌드(테스트 제외, CI/Docker와 동일)./gradlew --exclude-task test build
로컬 실행(local,my)./gradlew localBootRun
특정 프로파일 실행./gradlew bootRun --args="--spring.profiles.active=dev,my"
전체 테스트./gradlew test
단일 테스트./gradlew test --tests "com...AmadeusTest"
의존성 트리(예: bouncycastle 출처 추적)./gradlew dependencies --configuration runtimeClasspath
Docker 이미지 빌드docker build -t air-intl-adapter .
Swagger UI(local/dev/qa)http://localhost:8000/swagger-ui.html

더 많은 표는 quick-reference 참조.


9. 자가 점검 문제

Q1. ./gradlew localBootRun을 쳤을 때 실제로 어떤 프로파일이 켜지고, my는 어디서 오나?

Q2. 로컬에서 AWS 자격증명 없이 앱이 부팅에 성공하는 이유는?

Q3. release-staging 태그를 푸시하면 어느 AWS 계정에 배포되는가?

Q4. T'way SEED 암호화 코드가 컴파일되려면 빌드/도커에서 무엇이 보장돼야 하나?

Q5. Dockerfile은 EXPOSE 8080인데 트래픽이 안 들어온다. 어디를 봐야 하나?


10. 핵심 요약

  • 단일 모듈 단일 jar: 11개 공급사가 한 프로세스로 뜬다. 개별 배포 불가. (system-architecture)
  • 빌드: Gradle 8.11 Wrapper + Kotlin 2.0 + Java 21 + Spring Boot 3.3. -Xjsr305=strict로 null 엄격.
  • 비밀값: AWS Secrets Manager(<env>/air-intl-adapter[/공급사]), 전부 optional:이라 미접속 시에도 부팅 OK.
  • 프로파일: local≈dev(둘 다 dev 시크릿). prod/staging만 Slack/Sentry on, Swagger off.
  • 배포: master→release-qa 자동태그→CD. 태그명으로 환경 분기. qa만 DEV 계정, 나머지는 PROD 계정(주의).
  • 벤더 jar: libs/TwaySEED.jar는 git·도커 빌드 컨텍스트에 반드시 존재해야 함.

관련 노트: configuration-and-infra · quick-reference · resilience-and-events · async-coroutines · landmines · tway-protocol