STUDY/CLOUD

[๊ฐ•์˜] ๋”ฐ๋ผํ•˜๋ฉฐ ๋ฐฐ์šฐ๋Š” ๋„์ปค์™€ CIํ™˜๊ฒฝ -6

ozllzL 2022. 6. 28. 02:50

์ด์ „ : ํ”„๋ก ํŠธ๋งŒ ์‚ฌ์šฉ

์ด๋ฒˆ : ๋ฐฑ์—”๋“œ์™€ DB๋„ ๊ตฌํ˜„ํ•ด์„œ ๋งŽ์€ ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉ

 

๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์„ค๊ณ„ ๊ฐ€๋Šฅ -> ์ฒซ ๋ฒˆ์งธ ๋ฐฉ์‹์œผ๋กœ ๊ฐ•์˜ ์ง„ํ–‰

  • Nginx์˜ Proxy๋ฅผ ์ด์šฉํ•œ ์„ค๊ณ„
  • Nginx๋Š” ์ •์ ํŒŒ์ผ์„ ์ œ๊ณต๋งŒ ํ•ด์ฃผ๋Š” ์„ค๊ณ„

ํฐ๊ทธ๋ฆผ

๐Ÿ’ซNode JS ๊ตฌ์„ฑํ•˜๊ธฐ

backendํด๋” ๋งŒ๋“ค๊ณ  ์—ฌ๊ธฐ์„œ npm init

 

nodemon์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ  : ๋…ธ๋“œ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด๋„ ๋ฐ”๋กœ ์ค‘๋‹จ, ์žฌ๊ฐ€๋™ ์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด

db.js

const mysql = require("mysql");
const pool = mysql.createPool({
	connectionLimit: 10,
	host: 'mysql',
	user: 'root',
	password: 'johnahn',
	database: 'myapp'
});
exports.pool = pool;

 

server.js

const express = require("express");
const bodyParser = require('body-parser');

const db = require('./db');

const app = express();

app.user(bodyParser.json());

db.pool.query(`CREATE TABLE lists (
id INTEGER AUTO_INCREMENT,
value TEXT,
PRIMARY KEY (id))`,
    (err, results, fileds) => {
        console.log('results', results)
    })

ap.get('/api/values', function (req, res) {
    db.pool.query(`SELECT * FROM lists;`,
    (err, results, fileds) => {
        if (err)
            return res.status(500).send(err)
        else
            return res.json({ success: true, value: req.body.value })
    }
})

ap.post('/api/values', function (req, res, next) {
    db.pool.query(`INSERT INTO lists (vlaue) VALUES("${req.body.value}"))`,
    (err, results, fileds) => {
        if (err)
            return res.status(500).send(err)
        else
            return res.json({ success: true, value: req.body.value })
    }
})

app.listen(5000, () => {
    console.log('์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์„œ๋ฒ„ 5000๋ฒˆ ํฌํŠธ์—์„œ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค.')
})

๐Ÿ’ซReact JS ๊ตฌ์„ฑํ•˜๊ธฐ

frontend ํด๋” ์ƒ์„ฑ

npx create-react-app frontend ๋ช…๋ น์–ด๋กœ react ํŒŒ์ผ ์ƒ์„ฑ

App.js

axios ์•ˆ๋ผ์„œ ํ•œ์ฐธ ํ—ค๋งค๋‹ค ์ง์ ‘ ์„ค์น˜ํ–ˆ๋Š”๋ฐ ๋‹ด๊ฐ•์˜์— ์ž˜๋ชป๋๋‹ค๊ณ  ์•Œ๋ ค์ฃผ์‹ฌ ๊ฐœํ‚น๋ฐ›๋Š”๋‹ค

import logo from './logo.svg';
import './App.css';
import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {

    useEffect(() => {
        axios.get(`/api/values`)
            .then(response => {
                console.log('response', response.data)
                setLists(response.data)
            })
    }, [])

    const [lists, setLists] = useState([])
    const [value, setValue] = useState("")


    const changeHandler = (event) => {
        setValue(event.currentTarget.value)
    }

    const submitHandler = (event) => {
        event.preventDefault();
        axios.post(`/api/value`,
            { value: value })
            .then(response => {
                if (response.data.success) {
                    console.log('response.data', response.data)
                    setLists([...lists, response.data])
                    setValue("");
                } else {
                    alert("๊ฐ’์„ DB์— ๋„ฃ๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")
                }
            })
    }
    return (
        <div className="App">
            <header className="App-header">
                <img src={logo} className="App-logo" alt="logo" />
                <div className="container">
                    {lists && lists.map((list, index) => (
                        <li key={index}>{list.value}</li>
                    ))}
                    <form className="example" onSubmit={submitHandler}>
                        <input
                            type="text"
                            placeholder="์ž…๋ ฅํ•ด์ฃผ์„ธ์š”..."
                            onchange={changeHandler}
                            value={value}
                        />
                        <button type="submit">ํ™•์ธ</button>
                    </form>
                </div>
            </header>
        </div>
    );
}

export default App;

 

๐Ÿณ for ๋ฆฌ์•กํŠธ ์•ฑ ๋„์ปค ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

default.conf ๋ฅผ ์ด์š”์•Ÿ์—ฌ index.html์ด home์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ค€๋‹ค

server{
	listen 3000;
	location / {
		root /usr/share/nginx/html;

		index index.html index.htm;

		try_files $uri $uri/ /index.html;
	}
}

 

Dockerfile

FROM node:alpine as builder
WORKDIR /app
COPY ./package.json ./
RUN npm install
COPY ./ ./
RUN npm run build


FROM nginx
EXPOSE 3000
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/build /usr/share/nginx/html

 

๐Ÿณ for ๋…ธ๋“œ ์•ฑ ๋„์ปค ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

 

 

mysql ํด๋” ์ƒ์„ฑ

 

Dockerfile.dev

์œ„ ํŒŒ์ผ๊ณผ "start"->"dev"ํ•˜๋‚˜ ์ฐจ์ด

 

Dockerfile

Dockerfile.dev์—์„œ "start"๋งŒ ๊ทธ๋Œ€๋กœ ๋‘ 

 

๐Ÿณ for MySQL ๋„์ปค ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

Dockerfile

FROM mysql:5.7
ADD ./my.cnf /etc/mysql/conf.d/my.cnf

 

my.cnf

ํ•œ๊ธ€ ๊นจ์ง ํ˜„์ƒ ๋ฐฉ์ง€ ์œ„ํ•จ

 

initialize.sql

DROP DATABASE IF EXIST myapp;

CREATE DATABASE myapp;
USE myapp;

CREATE TABLE lists(
	id INTEGER AUTO_INCREMENT,
	value TEXT,
	PRIMARY KEY (id)
)

 

๐Ÿณ for Nginx ๋„์ปค ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

 

nginx ํด๋” ์ƒ์„ฑ

ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์— ๋”ฐ๋ผ ์ •์ ํŒŒ์ผ์„ ์›ํ•˜๋ฉด React.js API ์š”์ฒญ์ด๋ฉด Node.js๋กœ ๋ณด๋‚ธ๋‹ค.

->๊ธฐ์ค€: /api๋กœ ์‹œ์ž‘ํ•˜๋Š”์ง€, /๋กœ ์‹œ์ž‘ํ•˜๋Š”์ง€

 

default.conf

React.js๋กœ ๋ณด๋‚ผ ๊ฒƒ๊ณผ Node.js๋กœ ๋ณด๋‚ผ ๊ฒƒ ๋ถ„๋ฆฌ

Dockerfile

FROM nginx
COPY ./default.conf /etc/nginx/conf.d/default.conf

 

๐ŸŽ‡ Docker Compose ํŒŒ์ผ ์ž‘์„ฑํ•˜๊ธฐ

version: "3"
services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    volumes:
      - /app/node_modules
      - ./frontend:/app
    stdin_open: true

  nginx:
    restart : always #์žฌ์‹œ์ž‘ ์ •์ฑ…, ํ•ญ์ƒ ์žฌ์‹œ์ž‘
    build:
      context: ./nginx
      dockerfile: Dockerfile
    ports:
      - "3000:80"

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile.dev
    container_name: app_backend
    volumes:
      - /app/node_modules
      - ./backend:/app

  mysql:
    build: ./mysql
    restart: unless-stopped #๊ฐœ๋ฐœ์ž๊ฐ€ ์ž„์˜๋กœ ๋ฉˆ์ถ”๋ ค๊ณ  ํ•˜๋Š”๊ฑฐ ์•„๋‹ˆ๋ฉด ํ•ญ์ƒ ์žฌ์‹œ์ž‘
    container_name: app_mysql    
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/mysql_data:/var/lib/mysql
      - ./mysql/sqls/:/docker-entrypoint-initdb.d/
    environment:
      MYSQL_ROOT_PASSWORD: johnahn
      MYSQL_DATABASE: myapp

docker-compose up ์œผ๋กœ ์‹คํ–‰

๋นก๋Œ€๊ฐ€๋ฆฌ์˜ ์˜ค๋ฅ˜์ผ๊ธฐ

1. nginx ์ŠคํŽ ๋ง ํ‹€๋ฆผ nginix๋ผ๊ณ  ์˜คํƒ€๋ƒ„(์—”์ง€๋‹‰์Šค ๋งž์ž๋‚˜..)

2. "3306":"3306" ํ•จ 

3. ์ฃผ์„๋‹ค๋А๋ผ ํ•œ๊ธ€ ์จ๋†“๊ณ  utf-8๋กœ ์ €์žฅ ์•ˆํ•จ

๐Ÿ”Š Volume์„ ์ด์šฉํ•œ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ๋ฐ์ดํ„ฐ ์œ ์ง€ํ•˜๊ธฐ

์ด์ „ : ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ, ์ปจํ…Œ์ด๋„ˆ ์‚ญ์ œ ์‹œ ์ปจํ…Œ์ด๋„ˆ ์•ˆ์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋œ๋‹ค.

 

-> ์˜์†์„ฑ์ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š”? -> Volumes

ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์‹œ์Šคํ…œ์˜ ๋„์ปค์— ์˜ํ•ด์„œ๋งŒ ํ†ต์ œ๋˜๋Š” ๋„์ปค Area์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋œ๋‹ค

-> ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ๋ผ์ง€์ง€ ์•Š๋Š”๋‹ค.