diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9abf4ee --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,75 @@ +name: Continuous Deploy with GitHub Actions + S3 & EC2 + +on: + push: + branches: + - feature/* + pull_request: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout current commit (${{github.sha}}) + uses: actions/checkout@v3 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: Cache Dependencies + uses: actions/cache@v3 + id: cache + with: + # node_modules 폴더 검사 + path: node_modules + # 아래 키값으로 cache가 되있는지 확인 + key: npm-packages-${{ hashFiles('**/package-lock.json') }}\ + + - name: Generate Environment Variables File + env: + SECRET_KEY: ${{ secrets.SECRET_KEY }} + MONGODB_URL: ${{ secrets.MONGODB_URL }} + MY_AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} + MY_AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_IMAGE_BUCKET: ${{ secrets.AWS_IMAGE_BUCKET }} + DEFAULT_PROFILE_IMG_URL: ${{ secrets.DEFAULT_PROFILE_IMG_URL }} + DEFAULT_POST_IMG_URL: ${{ secrets.DEFAULT_POST_IMG_URL }} + + run: | + echo "SECRET_KEY: $SECRET_KEY" >> .env + echo "MONGODB_URL=$MONGODB_URL" >> .env + echo "MY_AWS_ACCESS_KEY=$MY_AWS_ACCESS_KEY" >> .env + echo "MY_AWS_SECRET_KEY=$MY_AWS_SECRET_KEY" >> .env + echo "DEFAULT_PROFILE_IMG_URL=$DEFAULT_PROFILE_IMG_URL" >> .env + echo "NEXT_PUBLIC_DEFAULT_POST_IMG=$DEFAULT_POST_IMG_URL" >> .env + + - name: Install Dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Build Next App + run: | + npm run build + env: + CI: false + + - name: Zip Build File + run: zip -qq -r ./ONEHOUR.zip . -x "node_modules/*" + shell: bash + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Upload to S3 + run: | + aws s3 cp --region ap-northeast-2 ./ONEHOUR.zip s3://${{ secrets.AWS_S3_BUCKET }}/ONEHOUR.zip + + - name: Code Deploy + run: aws deploy create-deployment --application-name ${{ secrets.CODEDEPLOY_APPLICATION }} --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name ONEHOUR --s3-location bucket=${{ secrets.AWS_S3_BUCKET }},bundleType=zip,key=ONEHOUR.zip diff --git a/appspec.yml b/appspec.yml new file mode 100644 index 0000000..ad31454 --- /dev/null +++ b/appspec.yml @@ -0,0 +1,18 @@ +version: 0.0 +os: linux + +files: + - source: / + destination: /home/ubuntu/deploy + overwrite: yes +file_exists_behavior: OVERWRITE +permissions: + - object: /home/ubuntu/deploy + owner: root + group: root + mode: 755 +hooks: + AfterInstall: + - location: ./deploy.sh + timeout: 1000 + runas: root diff --git a/components/navigator/Navigator.tsx b/components/navigator/Navigator.tsx index 3fb5405..188cae0 100644 --- a/components/navigator/Navigator.tsx +++ b/components/navigator/Navigator.tsx @@ -84,8 +84,7 @@ const Navigator = (props: { children: ReactNode }) => { onClick={() => { router.push("/dashboard"); }} - className={styles.logoWrapper} - > + className={styles.logoWrapper}> logoImage
@@ -111,18 +110,15 @@ const Navigator = (props: { children: ReactNode }) => { router.push("/dashboard/challenges"); }} className={`${ - pathname?.includes("challenges") && - styles.challengeButtonSelected - } ${styles.challengeButton}`} - > + pathname?.includes("ongoing") && styles.challengeButtonSelected + } ${styles.challengeButton}`}>

{pathname?.includes("challenges") ? "LET’S GO !" : "START CHALLENGE"}

+ className={styles.challengeIcon}>
@@ -133,8 +129,7 @@ const Navigator = (props: { children: ReactNode }) => { href={menu.path} className={`${styles.menuList} ${ menu.path === pathname && styles.selected - }`} - > + }`}> {menu.icon}
{menu.name}
@@ -150,8 +145,7 @@ const Navigator = (props: { children: ReactNode }) => { key={index} onClick={() => { if (menu === "로그아웃") onLogout(); - }} - > + }}>
{menu}
))} diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..f30463f --- /dev/null +++ b/deploy.sh @@ -0,0 +1,9 @@ +REPOSITORY=/home/ubuntu/deploy + +cd $REPOSITORY + +sudo npm install + +pm2 kill + +sudo npm run deploy \ No newline at end of file diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..ca07b3c --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,18 @@ +module.exports = { + apps: [ + { + name: "ONEHOUR", // 앱의 이름 + script: "npm", // Next.js 스크립트 경로 + args: "start", // Next.js 앱을 시작할 때 사용할 인수 + exec_mode: "cluster", // 실행 모드: cluster 또는 fork 중 선택 + instances: "2", // 클러스터 모드에서 실행할 인스턴스 수 (CPU 코어 수만큼) + interpreter: "bash", + autorestart: true, // 프로세스 자동 재시작 활성화 + watch: true, // 파일 변경 감지 활성화 (개발 중에만 활용) + max_memory_restart: "2G", // 2GB 이상 메모리 사용 시 재시작 + env_prodection: { + NODE_ENV: "production", // Node.js 환경 설정 + }, + }, + ], +}; diff --git a/package.json b/package.json index 1eb0d94..a58fc24 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "deploy": "pm2 start ecosystem.config.js --env production" }, "dependencies": { "@redux-devtools/extension": "^3.2.5", diff --git a/pages/api/challengers.js b/pages/api/challengers.js index 73429e1..6f073a0 100644 --- a/pages/api/challengers.js +++ b/pages/api/challengers.js @@ -27,7 +27,7 @@ export default async function handler(req, res) { } } else if (req.method === "POST") { try { - let { postTitle, postContent, postImgUrl, ...posts } = req.body; + let { postTitle, postContent, postImgUrl } = req.body; // 1. 헤더에 있는 엑세스토큰을 디코드하여 현재 로그인 중인 유저 ID를 얻음 let atHeader = req.headers["authorization"]; let acToken = atHeader.substr(7); @@ -55,7 +55,7 @@ export default async function handler(req, res) { // 4. 그 유저 필드의 이메일과 닉네임 + 게시글 제목과 본문 내용 + 날짜 정보를 newPost 변수에 객체로 저장 // default post img 로 변경해야함 - let defaultImgUrl = `https://${process.env.MY_AWS_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/default-profile.jpg`; + let defaultImgUrl = process.env.DEFAULT_PROFILE_IMG_URL; if (postImgUrl === "") { postImgUrl = defaultImgUrl; diff --git a/pages/api/image-upload.js b/pages/api/image-upload.js index 0987c11..b064248 100644 --- a/pages/api/image-upload.js +++ b/pages/api/image-upload.js @@ -12,7 +12,7 @@ export default async function handler(req, res) { const s3 = new aws.S3(); const urlResponse = await s3.createPresignedPost({ // S3 버킷의 presigned Url 생성 - Bucket: process.env.MY_AWS_BUCKET_NAME, + Bucket: process.env.AWS_IMAGE_BUCKET, Fields: { key: imgType + "/" + fileName }, // 파일 저장 경로 - aws의 presigned Url을 설정할 때 사용됨 Expires: 60, // seconds Conditions: [