jayeon 프로젝트 생성 [1] front & back 생성

📘Express 프로젝트 생성

jayeon 프로젝트에서 front가 vue라면 backend 역할은 express로 구현하려고 한다.

jayeon-back 폴더를 생성한 후

npm init

명령어로 package.json 파일을 생성한다.
질문이 몇개 나오는데 그냥 다 enter치고 넘어가도 된다.

그럼 다음과 같이 파일이 생성된다.

그 후에 server.js 파일을 생성해준다.

npm install express

명령어를 통해 express를 추가해주고

https://expressjs.com/ko/starter/hello-world.html

링크를 들어가면 Hello world 예제가 있다 해당 코드를 그대로 복사하여 server.js 파일에 붙여 넣어주자.

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
node ./server.js

명령어로 실행하면

서버가 실행되며

http://localhost:3000/
에서 확인할 수 있는 back end 서버가 생성되었다.

const express = require('express')
const app = express()
const port = 3000

...

app.get('/api/user', (req, res)=>{
    res.send({
        id: 1,
        name: '최준호'
    });
});

/user url을 추가하여 정보를 넘기도록 만들어 놓자

📗Front 수정

npm install -g @vue/cli
vue create front

vue3로 선택해서 진행

npm i axios

로 axios를 설치해주고

<template>
  <div class="app">
    <div v-if="state.login">
      로그인되었습니다.
    </div>
    <div v-else>
      <span>아이디</span>
      <input type="text" id="id" />
      <span>비밀번호</span>
      <input type="password" id="pw">
      <hr/>
      <button>로그인</button>
    </div>
  </div>
</template>

<script>
import { reactive } from "vue"
import axios from 'axios';

export default {
  setup(){
    const state = reactive({
      login : false
    });

    axios.get('/api/user').then((res)=>{
      console.log(res);
    });

    return {state};
  }
}
</script>

vue 내용을 다음과 같이 수정해주자.

aixos로 /api/user를 요청해도 반환이 안될 것이다. 앞에 url에서 host 정보가 필요하기 때문인데 그냥 바로 요청하면 CORS 에러가 난다 그래서 처리해보자!

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer:{
    proxy:{
        "/api":{
            target: "http://localhost:3000"
        }
    }
}  
})

vue.config.js파일을 생성하여 다음과 같이 작성하였다. "/api"로 요청하는 부분을 프록시 기술을 사용하여 "http://localhost:3000"로 우회하여 요청한다는 것이다.

이제 새로고침을 해서 개발자 도구를 키면

원하는 데이터가 날라온다.

<template>
  <div class="app">
    <div v-if="state.login">
      로그인되었습니다. {{state.account.name}}</div>
    <div v-else>
      <span>아이디</span>
      <input type="text" id="id" v-model="state.form.id" />
      <span>비밀번호</span>
      <input type="password" id="pw" v-model="state.form.pw" />
      <hr/>
      <button @click="submit()">로그인</button>
    </div>
  </div>
</template>

<script>
import { reactive } from "vue"
import axios from 'axios';

export default {
  setup(){
    const state = reactive({
      account: {
        id : null,
        name : ''
      },
      form:{
        id : '',
        pw : ''
      }
      
    });

    const submit = () => {
      const args = {
          id : state.form.id,
          pw : state.form.pw
      };
      axios.post('/api/user', args);
    }

    axios.get('/api/user').then((res)=>{
      state.account = res.data;
    });

    return {state};
  }
}
</script>

front에서 post로 user로 로그인 요청하도록 추가한다.

📘back 수정

npm i body-parser

request에서 body를 파싱하기 위해 body-parser를 설치하고

const bodyParser = require('body-parser')
app.use(bodyParser.json())

app.post('/api/user', (req,res)=>{
    const id = req.body.id;
    const pw = req.body.pw;

    console.log(id, pw);
});

server.js에 추가해준다.

🐳Docker 작성

FROM node:slim

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . /app

CMD [ "npm", "run", "prod"]

front

FROM node:slim

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . /app

CMD [ "node", "server.js"]

back

을 작성하여 hub에 올린 뒤

version: '3.7'
  
services:
    jayeon:
        image: "ililil9482/jayeon-front:1.0"
        container_name: jayeon
        expose:
            - 8080

networks:
    default:
        external:
            name: portfolio

front

version: '3.7'
  
services:
    jayeon-back:
        image: "ililil9482/jayeon-back:1.0"
        container_name: jayeon-back
        expose:
            - 3001

networks:
    default:
        external:
            name: portfolio

back

docker-compose로 작성하여 서버를 올릴 수 있도록 수정했다.

👨‍💼Jenkins 작성

pipeline {
    agent any

    stages {
        stage('Down') {
            steps {
sshPublisher(publishers: [
  sshPublisherDesc(configName: 'aws',
  transfers: [
    sshTransfer(cleanRemote: false,
    excludes: '',
    execCommand: 'cd /project/jayeon && sudo docker-compose down',
    execTimeout: 120000,
    flatten: false,
    makeEmptyDirs: false,
    noDefaultExcludes: false,
    patternSeparator: '[, ]+',
    remoteDirectory: '',
    remoteDirectorySDF: false,
    removePrefix: '',
    sourceFiles: '')
  ],
  usePromotionTimestamp: false,
  useWorkspaceInPromotion: false,
  verbose: false)
])
            }
        }

        stage('Pull') {
            steps {
sshPublisher(publishers: [
  sshPublisherDesc(configName: 'aws',
  transfers: [
    sshTransfer(cleanRemote: false,
    excludes: '',
    execCommand: 'sudo docker pull ililil9482/jayeon-front:1.0',
    execTimeout: 120000,
    flatten: false,
    makeEmptyDirs: false,
    noDefaultExcludes: false,
    patternSeparator: '[, ]+',
    remoteDirectory: '',
    remoteDirectorySDF: false,
    removePrefix: '',
    sourceFiles: '')
  ],
  usePromotionTimestamp: false,
  useWorkspaceInPromotion: false,
  verbose: false)
])
            }
        }

        stage('Up') {
            steps {
sshPublisher(publishers: [
  sshPublisherDesc(configName: 'aws',
  transfers: [
    sshTransfer(cleanRemote: false,
    excludes: '',
    execCommand: 'cd /project/jayeon && sudo docker-compose up -d',
    execTimeout: 120000,
    flatten: false,
    makeEmptyDirs: false,
    noDefaultExcludes: false,
    patternSeparator: '[, ]+',
    remoteDirectory: '',
    remoteDirectorySDF: false,
    removePrefix: '',
    sourceFiles: '')
  ],
  usePromotionTimestamp: false,
  useWorkspaceInPromotion: false,
  verbose: false)
])
            }
        }
        
        
    }
}

front

pipeline {
    agent any

    stages {
        stage('Down') {
            steps {
sshPublisher(publishers: [
  sshPublisherDesc(configName: 'aws',
  transfers: [
    sshTransfer(cleanRemote: false,
    excludes: '',
    execCommand: 'cd /project/jayeon-back && sudo docker-compose down',
    execTimeout: 120000,
    flatten: false,
    makeEmptyDirs: false,
    noDefaultExcludes: false,
    patternSeparator: '[, ]+',
    remoteDirectory: '',
    remoteDirectorySDF: false,
    removePrefix: '',
    sourceFiles: '')
  ],
  usePromotionTimestamp: false,
  useWorkspaceInPromotion: false,
  verbose: false)
])
            }
        }

        stage('Pull') {
            steps {
sshPublisher(publishers: [
  sshPublisherDesc(configName: 'aws',
  transfers: [
    sshTransfer(cleanRemote: false,
    excludes: '',
    execCommand: 'sudo docker pull ililil9482/jayeon-back:1.0',
    execTimeout: 120000,
    flatten: false,
    makeEmptyDirs: false,
    noDefaultExcludes: false,
    patternSeparator: '[, ]+',
    remoteDirectory: '',
    remoteDirectorySDF: false,
    removePrefix: '',
    sourceFiles: '')
  ],
  usePromotionTimestamp: false,
  useWorkspaceInPromotion: false,
  verbose: false)
])
            }
        }

        stage('Up') {
            steps {
sshPublisher(publishers: [
  sshPublisherDesc(configName: 'aws',
  transfers: [
    sshTransfer(cleanRemote: false,
    excludes: '',
    execCommand: 'cd /project/jayeon-back && sudo docker-compose up -d',
    execTimeout: 120000,
    flatten: false,
    makeEmptyDirs: false,
    noDefaultExcludes: false,
    patternSeparator: '[, ]+',
    remoteDirectory: '',
    remoteDirectorySDF: false,
    removePrefix: '',
    sourceFiles: '')
  ],
  usePromotionTimestamp: false,
  useWorkspaceInPromotion: false,
  verbose: false)
])
            }
        }
        
        
    }
}

back

모두 작성하여 정상적으로 컨테이너가 실행되는 것까지 확인했다.

이제 개발해서 붙여보자!

.env 설정

  "scripts": {
    "serve": "vue-cli-service serve",
    "local": "vue-cli-service serve --mode local",
    "prod": "vue-cli-service serve --mode prod",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },

실행 명령어를 다음과 같이 local 모드와 prod 모드 2가지를 추가해주었다.

npm run local
npm run prod

다음과 같이 서버를 실행할 수 있으며 local로 실행시 기존 serve와 동일한 환경이지만 구분을 위해 추가해주었으며 prod의 경우 실제 서버에서 실행할 명령어로 추가하였다.

VUE_APP_API_URL = 'http://localhost:3001'

.env.local 파일을 만들어서 로컬에서는 내가 띄워둔 api로 요청하려고 하고

VUE_APP_API_URL = 'http://node.jayeonapple.com'

.env.prod prod로 실행 시 prod로 설정 값을 읽어오려고 한다.

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  
  devServer:{
    allowedHosts: "all",
    proxy:{
        "/api":{
            target: process.env.VUE_APP_API_URL ||"http://node.jayeonapple.com"
        }
    }
}  
})

vue.config.js에서 VUE_APP_API_URL 환변 변수 값을 불러와서 실행하도록 세팅해주었다.

진행하면서 에러 해결사항

docker로 컨테이너를 띄워 cli로 서버를 실행하려고 하니까 Invalid Host header 라는 에러 문구만 띄워져서 인터넷을 뒤져보니

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  
  devServer:{
    disableHostCheck: true,
    proxy:{
        "/api":{
            target: "http://node.jayeonapple.com"
        }
    }
}  
})

로 진행하면 된다고 해서 했는데 local에서도 에러가 나더라 그래서 더 찾아보니

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  
  devServer:{
    allowedHosts: "all",
    proxy:{
        "/api":{
            target: "http://node.jayeonapple.com"
        }
    }
}  
})

allowHosts: "all"로 이번에 변경되었다고 한다. 이렇게 사용하니 더이상 에러가 노출되지 않고 잘 실행되었다!