ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [๊ฐ•์˜] ๋”ฐ๋ผํ•˜๋ฉฐ ๋ฐฐ์šฐ๋Š” ๋„์ปค์™€ CIํ™˜๊ฒฝ -6
    STUDY/CLOUD 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์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋œ๋‹ค

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

Designed by Tistory.