Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.gitignore
.gradle
build
out
**/node_modules
**/.idea
**/*.iml
**/.DS_Store
87 changes: 87 additions & 0 deletions .github/workflows/release-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Release Deploy

on:
push:
branches: ["release"]
workflow_dispatch: {}

env:
AWS_REGION: us-west-2
ACCOUNT_ID: 590184104064
ECR_REPOSITORY: popcong-server
REGISTRY: ${{ env.ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com
IMAGE_TAG: ${{ github.sha }}

concurrency:
group: release-deploy
cancel-in-progress: true

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read

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

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build & Push (linux/amd64)
uses: docker/build-push-action@v6
with:
platforms: linux/amd64
push: true
context: .
file: ./Dockerfile
tags: |
${{ env.REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
${{ env.REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest

deploy:
needs: build-and-push
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Connect & Deploy on EC2
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
set -euo pipefail

AWS_REGION="${{ env.AWS_REGION }}"
ACCOUNT_ID="${{ env.ACCOUNT_ID }}"
REGISTRY="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"

echo "[EC2] ECR 로그인"
aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin "$REGISTRY"

echo "[EC2] 디렉토리 이동"
cd ~/popcong

echo "[EC2] 최신 이미지 Pull"
docker compose pull

echo "[EC2] 재기동"
docker compose up -d --remove-orphans

echo "[EC2] 상태 확인"
docker compose ps
docker ps
57 changes: 57 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# amazoncorretto:21-alpine 이미지를 베이스로 사용 / 컨테이너 내부 작업 디렉토리는 app으로 함
FROM amazoncorretto:21-alpine AS builder
WORKDIR /app

# Gradle 캐시 최적화를 위한 복사
COPY gradlew gradle.properties* settings.gradle* build.gradle* /app/
COPY gradle /app/gradle

# Gradle Wrapper 실행 권한 부여 및 버전 확인
RUN chmod +x gradlew && ./gradlew --version

# 실제 소스코드 복사
COPY . /app

# 이미지 빌드
RUN ./gradlew clean bootJar -x test

# Spring Boot layertools로 레이어 추출
RUN java -Djarmode=layertools -jar $(find build/libs -name "*.jar" | head -n 1) extract --destination /app/layers

# Runtime
# 런타임에서도 같은 openjdk 이미지 사용
FROM amazoncorretto:21-alpine

# 일반 사용자로 실행
RUN addgroup -S popcong && adduser -S popcong -G popcong -u 1001

# 런타임 작업 디렉토리 지정
WORKDIR /app

# Spring Boot layertools 사용한 경우 설정하는 부분
# 의존성 레이어
COPY --from=builder /app/layers/dependencies/ ./
# Spring Boot 런처 레이어
COPY --from=builder /app/layers/spring-boot-loader/ ./
# 스냅샷 의존성 레이어
COPY --from=builder /app/layers/snapshot-dependencies/ ./
# 애플리케이션 코드 레이어: 가장 자주 바뀌는 부분
COPY --from=builder /app/layers/application/ ./

# 한국 환경 기본 환경변수 설정
ENV TZ=Asia/Seoul \
LANG=ko_KR.UTF-8 \
LANGUAGE=ko_KR:ko:en \
LC_ALL=ko_KR.UTF-8 \
SPRING_PROFILES_ACTIVE=prod \
SERVER_PORT=8080 \
JAVA_OPTS="-XX:MaxRAMPercentage=75 -XX:InitialRAMPercentage=50 -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Seoul"

# 컨테이너에서 노출할 포트
EXPOSE 8080

# root가 아닌 spring 사용자로 실행
USER popcong

# JarLauncher로 레이어 디렉토리 구조에 맞춰 앱 부팅 (Spring Boot layertools 사용한 경우)
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -cp /app org.springframework.boot.loader.launch.JarLauncher"]
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

//import static popcong.app.infra.config.security.SignInRedirectCaptureFilter.ALLOW_LIST;
Expand All @@ -39,10 +38,9 @@ public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler
private String frontendBaseUrl;

@Value("${app.frontend.path.main}")
private String mainPath;

private String mainPath; // home
@Value("${app.frontend.path.signup}")
private String signupPath;
private String signupPath; // signup

@Value("${app.cookie.domain}")
private String cookieDomain;
Expand Down Expand Up @@ -87,18 +85,19 @@ public void onAuthenticationSuccess(
log.info("accessToken: {}", accessToken);
log.info("refreshToken: {}", refreshToken);

// 레거시 이름(예전 소문자) 먼저 제거
// 이름 모두 제거
removeCookie(response, "access_token", "/");
removeCookie(response, "refresh_token", "/");

// 혹시 동일 이름이 다른 속성으로 남아있을 수 있으니 현행 이름도 한번 초기화
removeCookie(response, accessCookieName, accessCookiePath);
removeCookie(response, refreshCookieName, refreshCookiePath);

// 현행 이름으로만 심기
// 현재 이름으로만 심기
addCookie(response, accessCookieName, accessToken, accessCookiePath, accessCookieMaxAge);
addCookie(response, refreshCookieName, refreshToken, refreshCookiePath, refreshCookieMaxAge);

response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");

String desired = readCookie(request, "login_redirect")
.or(() -> Optional.ofNullable(request.getParameter("redirect_uri")))
.or(() -> Optional.ofNullable(request.getHeader("Origin")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ public enum CommonErrorCode implements ErrorCode {
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "C4004", "Invalid Http Method"),
NO_REQUIRED_FILES(HttpStatus.BAD_REQUEST.value(), "C4005", "필수 제출 파일이 누락되었습니다."),

// 405
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "C4054", "Invalid Http Method"),

// 5XX
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "C5001", "Internal Server Error");

Expand Down
18 changes: 1 addition & 17 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
spring:
profiles:
include: secret

jpa:
hibernate:
ddl-auto: update # 필요시 create
properties:
hibernate:
format_sql: true
show_sql: true
open-in-view: false

cloud:
aws:
profile: dev
region: us-east-2 # 버킷 리전
s3:
bucket: popcongbucket
include: secret