Skip to content

Commit 412eee9

Browse files
authored
Merge pull request #15 from ktb3-team4/feat/clay/image
Feat/clay/image
2 parents db5a644 + 23cd735 commit 412eee9

2 files changed

Lines changed: 67 additions & 43 deletions

File tree

.github/workflows/cicd.yaml

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ env:
1616
IMAGE_TAG: latest
1717

1818
jobs:
19+
discover-servers:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
matrix: ${{ steps.set-matrix.outputs.matrix }}
23+
steps:
24+
- name: Configure AWS credentials
25+
uses: aws-actions/configure-aws-credentials@v4
26+
with:
27+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
28+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
29+
aws-region: ${{ secrets.AWS_REGION }}
30+
31+
- name: Get frontend servers from AWS
32+
id: set-matrix
33+
run: |
34+
SERVERS=$(aws ec2 describe-instances \
35+
--filters "Name=tag:role,Values=frontend" "Name=instance-state-name,Values=running" \
36+
--query 'Reservations[*].Instances[*].[PublicIpAddress]' \
37+
--output json | jq -c 'flatten | map(select(. != null))')
38+
echo "Discovered servers: $SERVERS"
39+
echo "matrix={\"server\":$SERVERS}" >> $GITHUB_OUTPUT
40+
1941
build-and-push:
2042
runs-on: ubuntu-latest
2143
steps:
@@ -33,17 +55,18 @@ jobs:
3355
--build-arg NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }} \
3456
--build-arg NEXT_PUBLIC_SOCKET_URL=${{ secrets.NEXT_PUBLIC_SOCKET_URL }} \
3557
--build-arg NEXT_PUBLIC_AWS_REGION=${{ secrets.AWS_REGION }} \
36-
--build-arg NEXT_PUBLIC_AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
37-
--build-arg NEXT_PUBLIC_AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
38-
--build-arg NEXT_PUBLIC_AWS_S3_BUCKET=${{ secrets.AWS_S3_BUCKET }} \
58+
--build-arg NEXT_PUBLIC_BUCKET=${{ secrets.NEXT_PUBLIC_BUCKET }} \
3959
--build-arg NEXT_PUBLIC_CLOUDFRONT_DOMAIN=${{ secrets.CLOUDFRONT_DOMAIN }} \
4060
-t $IMAGE_NAME:$IMAGE_TAG .
4161
docker push $IMAGE_NAME:$IMAGE_TAG
4262
4363
deploy:
44-
needs: build-and-push
64+
needs: [build-and-push, discover-servers]
4565
runs-on: ubuntu-latest
4666
if: github.event_name == 'push'
67+
strategy:
68+
matrix: ${{ fromJson(needs.discover-servers.outputs.matrix) }}
69+
fail-fast: false # 한 서버 실패해도 다른 서버 계속 배포
4770
steps:
4871
- name: Set up SSH
4972
uses: webfactory/ssh-agent@v0.9.0
@@ -53,16 +76,28 @@ jobs:
5376
- name: Add known_hosts
5477
run: |
5578
mkdir -p ~/.ssh
56-
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
79+
ssh-keyscan -H ${{ matrix.server }} >> ~/.ssh/known_hosts
80+
81+
- name: Deploy to EC2 (${{ matrix.server }})
5782

58-
- name: Deploy to EC2
5983
run: |
60-
ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} 'bash -s' <<EOF
84+
ssh ${{ secrets.SERVER_USER }}@${{ matrix.server }} 'bash -s' <<EOF
6185
set -e
6286
DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
6387
DOCKER_TOKEN=${{ secrets.DOCKER_TOKEN }}
6488
IMAGE_NAME=\$DOCKER_USERNAME/front:latest
65-
89+
90+
echo ">>> Create .test file with environment variables"
91+
cat > /home/${{ secrets.SERVER_USER }}/app/.test <<ENVFILE
92+
NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}
93+
NEXT_PUBLIC_SOCKET_URL=${{ secrets.NEXT_PUBLIC_SOCKET_URL }}
94+
NEXT_PUBLIC_AWS_REGION=${{ secrets.AWS_REGION }}
95+
NEXT_PUBLIC_ACCESSKEY=${{ secrets.AWS_ACCESS_KEY_ID }}
96+
NEXT_PUBLIC_SECRETKEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}
97+
NEXT_PUBLIC_BUCKET=${{ secrets.NEXT_PUBLIC_BUCKET }}
98+
NEXT_PUBLIC_CLOUDFRONT_DOMAIN=${{ secrets.CLOUDFRONT_DOMAIN }}
99+
ENVFILE
100+
66101
echo ">>> Docker Hub login"
67102
echo "\$DOCKER_TOKEN" | docker login -u "\$DOCKER_USERNAME" --password-stdin
68103
@@ -74,10 +109,10 @@ jobs:
74109
cd /home/${{ secrets.SERVER_USER }}/app
75110
76111
echo ">>> docker compose down (if exists)"
77-
docker compose down || true
78-
112+
docker compose -f /home/${{ secrets.SERVER_USER }}/app/docker-compose.yml down || true
113+
79114
echo ">>> docker compose up -d"
80-
docker compose up -d
81-
115+
docker compose -f /home/${{ secrets.SERVER_USER }}/app/docker-compose.yml up -d
116+
82117
echo ">>> Deployed!"
83118
EOF

services/fileService.js

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
21
import axiosInstance from "./axios";
32
import { Toast } from "../components/Toast";
43

54
const CLOUDFRONT_DOMAIN = process.env.NEXT_PUBLIC_CLOUDFRONT_DOMAIN;
65

76
const S3_CONFIG = {
87
region: process.env.NEXT_PUBLIC_AWS_REGION,
8+
bucket: process.env.NEXT_PUBLIC_BUCKET,
9+
910
credentials: {
1011
accessKeyId: process.env.NEXT_PUBLIC_AWS_ACCESS_KEY_ID,
1112
secretAccessKey: process.env.NEXT_PUBLIC_AWS_SECRET_ACCESS_KEY,
@@ -15,7 +16,6 @@ const S3_CONFIG = {
1516

1617
class FileService {
1718
constructor() {
18-
this._s3Client = null;
1919
this.bucket = S3_CONFIG.bucket;
2020
this.uploadLimit = 50 * 1024 * 1024; // 50MB
2121

@@ -35,19 +35,6 @@ class FileService {
3535
};
3636
}
3737

38-
// S3 클라이언트 지연 초기화
39-
get s3Client() {
40-
if (!this._s3Client && S3_CONFIG.region) {
41-
this._s3Client = new S3Client({
42-
region: S3_CONFIG.region,
43-
credentials: S3_CONFIG.credentials,
44-
requestChecksumCalculation: "WHEN_REQUIRED",
45-
responseChecksumValidation: "WHEN_REQUIRED",
46-
});
47-
}
48-
return this._s3Client;
49-
}
50-
5138
// 파일 유효성 검사
5239
async validateFile(file) {
5340
if (!file) {
@@ -81,15 +68,8 @@ class FileService {
8168
return { success: true };
8269
}
8370

84-
// S3 직접 업로드
71+
// S3 직접 업로드 (퍼블릭 버킷 - 인증 불필요)
8572
async uploadFile(file, onProgress) {
86-
if (!this.s3Client) {
87-
return {
88-
success: false,
89-
message: "S3 클라이언트가 초기화되지 않았습니다.",
90-
};
91-
}
92-
9373
const validationResult = await this.validateFile(file);
9474
if (!validationResult.success) {
9575
return validationResult;
@@ -100,16 +80,25 @@ class FileService {
10080
const safeFileName = file.name.replace(/[^a-zA-Z0-9.-]/g, "_");
10181
const key = `images/${timestamp}_${safeFileName}`;
10282

103-
const command = new PutObjectCommand({
104-
Bucket: this.bucket,
105-
Key: key,
106-
Body: file,
107-
ContentType: file.type,
108-
});
83+
// S3 직접 업로드 (AWS SDK 없이 fetch 사용)
84+
const region = S3_CONFIG.region;
85+
const bucket = this.bucket;
86+
const s3Url = `https://${bucket}.s3.${region}.amazonaws.com/${key}`;
10987

110-
// 진행률 시뮬레이션 (AWS SDK v3 기본 미지원)
11188
if (onProgress) onProgress(10);
112-
await this.s3Client.send(command);
89+
90+
const response = await fetch(s3Url, {
91+
method: 'PUT',
92+
body: file,
93+
headers: {
94+
'Content-Type': file.type,
95+
},
96+
});
97+
98+
if (!response.ok) {
99+
throw new Error(`S3 업로드 실패: ${response.status} ${response.statusText}`);
100+
}
101+
113102
if (onProgress) onProgress(100);
114103

115104
// CloudFront URL 생성

0 commit comments

Comments
 (0)