1.    React๋กœ ํ™”๋ฉด ๊ฐœ๋ฐœ

์ž, ์ด์ œ๋ถ€ํ„ฐ REACT๋กœ ํ™”๋ฉด์„ ๊ฐœ๋ฐœ ํ•ด๋ณด์ž.

 

์•ž์—์„œ ์‚ฌ์ „ ์ค€๋น„์‹œ ํ”„๋กœ์ ํŠธ ํด๋”์ธ linkservice์•„๋ž˜์— npx create-react-app ์œผ๋กœ ์ด๋ฏธ client์„ ์ƒ์„ฑํ–ˆ๋‹ค.

#4. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ •

 

#4. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ •

1.1. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ • ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ๋Š” ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ๋‚˜๋ˆ„๊ณ ์ž client / server๋กœ ๊ตฌ๋ถ„ ํ•œ๋‹ค. โŽฎํ”„๋กœ์ ํŠธ ํด๋” : linkserver/ |-------- client ( React ๊ตฌ์กฐ) |-------- server ( Python๊ตฌ

firstvalue.tistory.com

 

1.1.  React ๊ธฐ๋ณธ์„ค์ •

๊ธฐ๋ณธ ํด๋”๊ตฌ์กฐ์—์„œ ํ•„์š” ์—†๋Š” ํŒŒ์ผ์„ ๋ชจ๋‘ ์ง€์šด๋‹ค.

      · public ํด๋” ๋ฐ‘์— index.html์„ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋‘ ์‚ญ์ œ

      · src ํด๋” ๋ฐ‘์— App.js, index.js ์„ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋‘ ์‚ญ์ œ

React ํŒŒ์ผ ๊ตฌ์กฐ๋ฅผ ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•œ๋‹ค๋ฉด,

1. index.js

src ํด๋”์— ๋ฉ”์ธ Script๋กœ ์—ฌ๊ธฐ์—์„œ HTML ํ…œํ”Œ๋ฆฟ ๋ฐ JavaScript์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ๋ Œ๋”๋ง์„ ํ•œ๋‹ค.

 

2. App.js

src ํด๋”์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•˜๋Š” Script์ด๋‹ค. ์‹ค์ œ๋กœ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๋‚ด์šฉ ๋“ฑ์€ ์—ฌ๊ธฐ์—์„œ ์ •์˜๋œ๋‹ค.

 

3. index.html

public ํด๋”์— ์žˆ์œผ๋ฉฐ, HTML ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์ด๋‹ค. ์ด ํŒŒ์ผ์ด ์ง์ ‘ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , index.js์— ์˜ํ•ด ๋ Œ๋”๋ง ๋œ ๊ฒฐ๊ณผ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค.

 

   ๐Ÿ“Œ App.js์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•˜๊ณ  -> index.js ์—์„œ ReactDOM.rendor ํ•˜๊ณ   -> index.html์—์„œ ํ‘œ์‹œ๋œ๋‹ค.

 

๋จผ์ €, App.js ์„ ์ˆ˜์ •ํ•œ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ํ•„์š” ์—†๋Š” ๋‚ด์šฉ ์ง€์šฐ๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ ํ•œ๋‹ค.

 


index.js ์—์„œ reportWebVitals ๋ถ€๋ถ„๊ณผ import index.css์„ ์ฃผ์„์ฒ˜๋ฆฌ ํ•œ๋‹ค.

   ๐Ÿ“Œ ์ฐธ๊ณ ๋กœ, reportWebVitals๋Š” Create React App์—์„œ ์„œ๋“œํŒŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ Index ํŒŒ์ผ์— ์จ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ

       reportWebVitals() ์— console.log์„ ๋„ฃ์–ด์ฃผ๋ฉด ๊ฐœ๋ฐœ์ฐฝ์„ ํ†ตํ•ด ์•ฑ์˜ ํผํฌ๋จผ์Šค ์‹œ๊ฐ„๋“ค์„ ๋ถ„์„ํ•˜์—ฌ objectํ˜•ํƒœ๋กœ

      ๋ณด์—ฌ์ค€๋‹ค.

 

 

์ด์   ํ„ฐ๋ฏธ๋„์—์„œ yarn start์„ ์‹คํ–‰ํ•˜๊ณ , ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ ‘์†ํ•ด๋ณด์ž.

1.2. ๋””์ž์ธ ๊ฐ€์ ธ์˜ค๊ธฐ

๋””์ž์ธ๊นŒ์ง€ ํ•˜๋ฉด 1์ธ ๊ฐœ๋ฐœ์ž๋กœ ๊ฐ€์žฅ ์ด์ƒ์ ์ด๋งŒ, ๊ทธ๋งŒํผ ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•˜๋‹ค. ๋‚˜๋„ ๋””์ž์ธ ๋งŒํผ์€ ๋งŽ์ด ๋ฐฐ์šฐ์ง€ ๋ชปํ•ด์„œ ์–•์€ ์ง€์‹์œผ๋กœ ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด ๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

๋””์ž์ธ์ด ํ•„์š” ์—†๋Š” ๋ถ„๋“ค์€ ๋””์ž์ธ ์„ค๋ช…๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ , ๊ฐœ๋ฐœ๋ถ€๋ถ„๋งŒ ๋ด๋„ ๋œ๋‹ค.

 

์šฐ์„  Bootstrap ๋ฌด๋ฃŒ ๋””์ž์ธ์„ ๊ฐ€์ง€๊ณ  ์™€์„œ ๋ณ€๊ฒฝํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

๋””์ž์ธ ์ฐธ์กฐ : https://getbootstrap.com/docs/5.3/examples/

์šฐ๋ฆฌ๋Š” Albumํ™”๋ฉด์„ ๊ฐ€์ง€๊ณ  ์‹œ์ž‘ํ•˜๊ณ ์ž ํ•œ๋‹ค. https://getbootstrap.com/docs/5.3/examples/album/ ์ ‘์†ํ›„ ์˜ค๋ฅธ์ชฝ ๋งˆ์šฐ์Šค ํด๋ฆญํ•ด์„œ ํŽ˜์ด์ง€ ์†Œ์Šค๋ณด๊ธฐ์—์„œ HTML์„ ๋ณต์‚ฌํ•˜์—ฌ index.html์— ๋ถ™์—ฌ ๋„ฃ๋Š”๋‹ค.

 

BootStrap ๋ฌด๋ฃŒ๋””์ž์ธ HTML์ฝ”๋“œ๋ฅผ index.html์— ๋ถ™ํžŒ๋‹ค.

 

Title๋“ฑ ํ…์ŠคํŠธ ๋ถ€๋ถ„์„ ๋นผ๊ฑฐ๋‚˜, ์ˆ˜์ •ํ•˜์‹œ๊ณ , HTML์—์„œ ๋ณธ๋ฌธ์— ํ•ด๋‹น๋˜๋Š” <main></main> ๊ณผ <footer></footer> ๋Š” ์‚ญ์ œํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์—์„œ ํ•„์š”ํ•œ ๋ถ€๋ถ„์€ ์ถ”๊ฐ€/์ˆ˜์ •/์‚ญ์ œํ•ด์„œ ์ดˆ๊ธฐ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•ด๋ณด์ž

 

<main></main> ๋ถ€๋ถ„์ด ๊ฐ€์žฅ ์ค‘์š”ํ•˜๋ฏ€๋กœ, ์†Œ์Šค๋‚ด ํ™•์ธํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.

 

<!doctype html>
<html lang="ko">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1">
  <meta name="description" content="">
  <meta name="generator" content="Hugo 0.88.1">
  <title>๋งํฌ์„œ๋น„์Šค</title>

  <!-- Bootstrap core CSS -->
  <link href="https://getbootstrap.kr/docs/5.1/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>

<body>
  <header>
    <div class="collapse bg-dark" id="navbarHeader">
      <div class="container">
        <div class="row">
          <div class="col-12">
            <h4 class="text-white">๋งํฌ์„œ๋น„์Šค</h4>
            <p class="text-muted">๊ฐœ๋ฐœ์€ ์—„์—ฐํžˆ ์กด์ค‘ ๋ฐ›์•„์•ผ ํ•  ๊ฐœ์ธ์˜ ์ฐฝ์ž‘๋ฌผ์ž…๋‹ˆ๋‹ค. 
            </p>
          </div>
        <div class="col-12">
          <h6 class="text-white">Developers</h4>
          <ul class="list-unstyled">
            <li class="text-muted">(์ฃผ)XXXXXXXX</li>
            <li class="text-muted">์‚ฌ์—…์ž๋ฒˆํ˜ธ:XXX-XX-xxxxx</li>
            <li class="text-muted">๋Œ€ํ‘œ์ „ํ™”:000-1111-2222</li>            
          </ul>
        </div>
      </div>

      </div>
    </div>
    <div class="navbar navbar-dark bg-dark shadow-sm">
      <div class="container">
        <a href="/" class="navbar-brand d-flex align-items-center">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor"
            stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="me-2"
            viewBox="0 0 24 24">
            <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
            <circle cx="12" cy="13" r="4" />
          </svg>
          <strong>๋งํฌ์„œ๋น„์Šค</strong>
        </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarHeader"
          aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
      </div>
    </div>
  </header>


  <main>
    <!-- ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ -->
    <section class="container" id="section">
    </section>    

    <div class="album py-4 bg-light" >
      <div class="container" id="root">
            <!--   ์—ฌ๊ธฐ์— ๋ณธ๋ฌธ๋‚ด์šฉ์„  ์ฒ˜๋ฆฌ -->
      </div>
    </div>
  </main>
  <!-- <footer class="text-muted py-2" id="footer">
  </footer> -->
</body>
</html>

๐Ÿ“Œ ๋””์ž์ธ ๋ฐ ํผ๋ธ”๋ฆฌ์‹ฑ์ด ๊ฐ€๋Šฅํ•˜์‹  ๋ถ„๋“ค์€ ๊ฐ์ž ๋‹ค๋ฅธ ๋””์ž์ธ์œผ๋กœ ํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

 

์ด์   ํ™”๋ฉด์ด ์ž˜ ์ถœ๋ ฅ๋˜๋Š”์ง€ ํ™•์ธ ํ•œ๋‹ค.


๋‹ค์ŒํŽธ์—๋Š” Component์ œ์ž‘์„ ํ•ด๋ณด์ž.

 

 

#6. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๋ฐ์ดํƒ€๋ฒ ์ด์Šค ๋ชจ๋ธ ์ž‘์—…)

 

#6. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๋ฐ์ดํƒ€๋ฒ ์ด์Šค ๋ชจ๋ธ ์ž‘์—…)

#5. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๊ธฐ๋ณธํ™˜๊ฒฝ๊ตฌ์„ฑ) #5. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๊ธฐ๋ณธํ™˜๊ฒฝ๊ตฌ์„ฑ) โŒ˜ server.py ( ์œ„์น˜: linkservice/server/server.py) --------------------------------------------------------------------------------------------------- app.py

firstvalue.tistory.com

 

์ด์ „ ํฌ์ŠคํŒ…์— ๋ฐ์ดํƒ€๋ฒ ์ด์Šค ๋ชจ๋ธ๋ง ์ž‘์—…์„ ํ–ˆ๊ณ , ์—ฌ๊ธฐ์„œ๋Š”  server.py์— flask_restful์—์„œ ์ œ๊ณต๋˜๋Š”

API Resource์„ ์ถ”๊ฐ€ํ•ด ๋ณด์ž.

 

โŒ˜ server.py ( ์œ„์น˜: linkservice/server/server.py)

----------------------------------------------------------------------------------------------------------

๊ธฐ์กด server.py ์— ์ถ”๊ฐ€ํ•ด๋ณด์ž.

import socket
from config.create_app import create_app
from flask import Flask
    #flask_restful์—์„œ API์™€  src ๋ฐ‘์— links.py์— class๋“ค์„ import ํ•œ๋‹ค.
from flask_restful import Api
from src.links import LinkRegister, LinksAll, LinkSelect, LinkRemove, LinkUpdate
from db_init import db

env = socket.gethostbyname(socket.gethostname())
if 'linkservice.com' in env :
   app = create_app('production')
else:
   app = create_app('development')
# print(app.config)
    # API ์„ ์–ธ
api = Api(app)
    #API์— Resource๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
api.add_resource(LinkRegister, '/api/link/create')
api.add_resource(LinksAll, '/api/links')
api.add_resource(LinkSelect, '/api/link/select/<int:id>')
api.add_resource(LinkRemove, '/api/link/delete/<int:id>')
api.add_resource(LinkUpdate, '/api/link/update/<int:id>')

if __name__ == "__main__":
# ORM
    db.init_app(app)
    app.run(debug=True, port=5000)

 

์ €์žฅ ํ›„ ์ด์ œ ์‹คํ–‰ํ•ด์„œ ํ™•์ธํ•ด๋ณด์ž.

 

โŽฎ์‹คํ–‰๋ฐฉ๋ฒ•

   1.     ํ„ฐ๋ฏธ๋„์—์„œ ๊ฐ€์ƒํ™” ์‹คํ–‰ ( source ./bin/activate )

   2.     python3 server.py ์‹คํ–‰

   3.     POSTMAN์—์„œ API ํ…Œ์ŠคํŠธ

 

์‹คํ–‰ํ›„ POSTMAN์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์ž.

1.1.  API ํ…Œ์ŠคํŠธ

โ˜‘๏ธŽ POST /api/link/create - ๋งํฌ์ •๋ณด ๋“ฑ๋ก

          Body์— JSON์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  ์ง„ํ–‰ํ•ด๋ณด์ž.

message์„ ์ •์ƒ์ ์œผ๋กœ return ๋ฐ›๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

 

โ˜‘๏ธŽ GET /api/links – ์ „์ฒด ๋งํฌ์ •๋ณด ์กฐํšŒ

โ˜‘๏ธŽ GET /api/link/select/<int:id>        - ID์— ํ•ด๋‹นํ•œ ๋งํฌ์ •๋ณด์กฐํšŒ

 

 

โ˜‘๏ธŽ PUT /api/link/update/<int:id>           - name์„ ์ˆ˜์ •ํ•ด๋ณด์ž.

 

โ˜‘๏ธŽ DELETE /api/link/delete/<int:id> - ID์— ํ•ด๋‹นํ•œ ๋งํฌ์ •๋ณด ์‚ญ์ œ

 

LINKS ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ CRUD API๊ฐ€ ์™„์„ฑ๋˜์—ˆ๋‹ค.

 

์ฃผ์š” ํ…Œ์ด๋ธ”์ธ LINKS์˜ CRUD API๊ฐ€ ์™„์„ฑ๋˜์—ˆ์œผ๋ฏ€๋กœ, ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‹ค์ œ ํ™”๋ฉด์„ ๊ตฌํ˜„์„ ํ•ด๋ณด๊ณ , ์•„์ง๊นŒ์ง€ DB๋ฐ API์„ค๊ณ„์— ๋น ์ง„ ๊ฒƒ์€ ํ™”๋ฉด์„ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ์ถ”๊ฐ€์ ์œผ๋กœ ์ œ์ž‘ํ•ด ๋ณด๊ธฐ๋กœ ํ•œ๋‹ค.

#5. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๊ธฐ๋ณธํ™˜๊ฒฝ๊ตฌ์„ฑ)

 

#5. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๊ธฐ๋ณธํ™˜๊ฒฝ๊ตฌ์„ฑ)

โŒ˜ server.py ( ์œ„์น˜: linkservice/server/server.py) --------------------------------------------------------------------------------------------------- app.py๋กœ ๋ณดํ†ต ๋ช…์นญ ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” server.py๋กœ ๋„ค์ž„์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์ง„ํ–‰ํ•œ๋‹ค. ์‹ค

firstvalue.tistory.com

Python Flask์˜ ๊ธฐ๋ณธํ™˜๊ฒฝ์ด ๊ตฌ์„ฑ๋˜์—ˆ๊ณ , ์ด์   ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ จ ์ž‘์—…์„ ์ง„ํ–‰ํ•œ๋‹ค.

 

SQLAlchemy๋Š” Python์—์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ORM(Object-Relational Mapping) ์ด๋ฉฐ, ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งคํ•‘(์—ฐ๊ฒฐ)ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” Flask์—์„œ ํ™•์žฅํ˜•ํƒœ๋กœ SQLALchemy์„ ์ง€์›ํ•˜๋Š” Flask-SQLALchemy ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

โŒ˜ db_init.py ( ์œ„์น˜: linkservice/server/db_init.py)

---------------------------------------------------------------------------------------------------------

db_init.py๋Š” db = SQLAlchemy() ์„ ์–ธ์„ ํ•ด์ฃผ๋Š” ๊ณตํ†ต ๋ชจ๋“ˆ๋กœ ํ™œ์šฉ ํ•œ๋‹ค.

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

โŒ˜ links_model.py ( ์œ„์น˜: linkservice/server/models/links_model.py)

---------------------------------------------------------------------------------------------------

์ด์ œ๋ถ€ํ„ฐ links ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ DB๋ชจ๋ธ์„ ์ œ์ž‘ํ•˜๊ณ , ๊ธฐ๋ณธ์ ์ธ id ๊ฐ’์œผ๋กœ ์‚ญ์ œ/์กฐํšŒ/์—…๋ฐ์ดํŠธ ๋“ฑ ํ•„์š”ํ•œ ๊ธฐ๋ณธ METHOD์„ ์ •์˜ํ•œ๋‹ค.

from db_init import db
from sqlalchemy import func

class LinkModel(db.Model):
	# DB ์Šคํ‚ค๋งˆ๊ฐ€ ๊ธฐ๋ณธ(public)์ด ์•„๋‹ˆ๋ฉด ๋ณ„๋„๋กœ ์„ ์–ธ์„ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
    __table_args__ = {'schema': 'aip'}
    
	# ๋ฌผ๋ฆฌ ํ…Œ์ด๋ธ”๋ช…
    __tablename__ = "links"
    
	# ์ปฌ๋Ÿผ ๋งตํ•‘
    
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    tag = db.Column(db.String(50))
    sort = db.Column(db.Integer)
    imageurl = db.Column(db.Text)
    linkurl = db.Column(db.Text)
    create_date = db.Column(db.Date)
    update_date = db.Column(db.Date)

    def __init__(self, id, name, tag, sort, imageurl, linkurl, create_date,update_date):
        self.id = id
        self.name = name
        self.tag = tag
        self.sort = sort
        self.imageurl = imageurl
        self.linkurl = linkurl
        self.create_date = create_date
        self.update_date = update_date

    def save(links):
        """
        ์ •๋ณด ์ €์žฅ    :param ์ •๋ณด ๊ฐ์ฒด
        """
        db.session.merge(links)
        db.session.commit()
        db.session.close()

	# Link์ •๋ณด ์ €์žฅ์‹œ sort ์ˆœ์„œ์˜ max๊ฐ’์„ ๊ตฌํ•œ๋‹ค. ๋งŒ์•ฝ null์ด๋ฉด 0 ์œผ๋กœ ๋ณ€๊ฒฝ
    # ํ™”๋ฉด์— ์ถœ๋ ฅ๋˜๋Š” ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ sortํ•ญ๋ชฉ์ด ํ•„์š”ํ•˜๋ฉฐ, ๋งํฌ์ •๋ณด ๋“ฑ๋ก์‹œ max๊ฐ’์„ ์ €์žฅํ•˜๊ธฐ์œ„ํ•ด์„œ ํ•„์š”
    
    @classmethod
    def max_sort(self):
      result = db.session.query(func.coalesce(func.max(self.sort), 0)).scalar()

      return result+1

	# ๊ธฐ๋ณธ์ ์ธ ์กฐํšŒ/์‚ญ์ œ/์—…๋ฐ์ดํŠธ ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. ( param : id )
    
    @classmethod
    # id๋กœ ์กฐํšŒ
    def find_by_id(self, id):
		links= db.session.query(self).filter_by(id=id).first()
        return links

    @classmethod
    # id๋กœ ์‚ญ์ œ    
    def delete_by_id(self, id):
		try:
        	db.session.query(self).filter_by(id=id).delete(synchronize_session=False)
      	except:
        	db.session.rollback()
      	finally:
        	db.session.commit()
        	db.session.close()

    @classmethod
    # id๋กœ values๊ฐ’์„ ์—…๋ฐ์ดํŠธ
    def update_by_id(self, id, values):
        db.session.query(self).filter_by(id=id).update(values)     
        db.session.commit()   
        db.session.close()     

    def __str__(self):
        return "[" + str(self.__class__) + "]: " + str(self.__dict__)

Links ํ…Œ์ด๋ธ”์˜ DB๋ชจ๋ธ๊ณผ ๊ธฐ๋ณธ ์ •๋ณด ์กฐํšŒ/์‚ญ์ œ/์—…๋ฐ์ดํŠธ ๊ด€๋ จ ๋ฉ”์„œ๋“œ๋„ ์ œ์ž‘์ด ๋๋‚ฌ์œผ๋‹ˆ,

์ด์   API ํ˜•ํƒœ๋กœ ์ œ๊ณตํ•˜๊ณ ์ž Resource๋ฅผ ์ œ์ž‘ํ•œ๋‹ค. 

 

1.1. Resource ์ œ์ž‘

โŒ˜ links.py ( ์œ„์น˜: linkservice/server/src/links.py)

---------------------------------------------------------------------------------------------------

links.py์—์„œ API ํ˜ธ์ถœ ์‹œ ์‹คํ–‰๋˜๋Š” class์„ ์ •์˜ํ•ด๋ณด์ž.

-       class LinkRegister(Resource) : ๋งํฌ์ •๋ณด๋ฅผ DB์— ๋“ฑ๋ก(Insert) ํ•œ๋‹ค.

-       class LinksAll(Resource) : ์ „์ฒด ๋งํฌ ์ •๋ณด๋ฅผ ์กฐํšŒํ•œ๋‹ค.

-       class LinkSelect(Resource) : ์„ ํƒํ•œ ๋งํฌ์ •๋ณด๋ฅผ ์กฐํšŒํ•œ๋‹ค..

-       class LinkRemove(Resource) : ์„ ํƒํ•œ ๋งํฌ์ •๋ณด๋ฅผ ์‚ญ์ œํ•œ๋‹ค..

-       class LinkUpdate(Resource) : ์„ ํƒํ•œ ๋งํฌ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค..

from db_init import db
from flask_restful import Resource, reqparse
from flask import jsonify, request
from flask_marshmallow import Marshmallow
from models.links_model import LinkModel
from datetime import date as date_function

ma = Marshmallow() #๊ฐ์ฒด๋ฅผ Python ๋ฐ์ดํ„ฐ ์œ ํ˜•์œผ๋กœ ์ง๋ ฌํ™”

class LinkSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = LinkModel

"""
POST /api/link/create -  ๋“ฑ๋ก resource
"""
class LinkRegister(Resource):
# ์ž…๋ ฅ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •
# DBํ•ญ๋ชฉ์— ๋งž๊ฒŒ ํŒŒ๋ผ๋ฉ”ํƒ€๋กœ ๋ฐ›์•„ ์ €์žฅํ•œ๋‹ค.
    parser = reqparse.RequestParser()
    parser.add_argument('name',type=str,required=True,help="ํ•„์ˆ˜ํ•ญ๋ชฉ์ž…๋‹ˆ๋‹ค.")
    parser.add_argument('tag',type=str)
    parser.add_argument('sort',type=str)     
    parser.add_argument('imageurl',type=str )      
    parser.add_argument('linkurl',type=str )    

    def post(self):
        # Request Parameter๋ฅผ dictionary ํ˜•ํƒœ๋กœ ์ €์žฅ
        data = LinkRegister.parser.parse_args()
        name = data['name']
        tag = data['tag']
        sort = LinkModel.max_sort() #Sort์ˆœ์„œ๋ฅผ ์œ„ํ•œ ๊ฐ’์œผ๋กœ ์ž…๋ ฅ์‹œ max๊ฐ’์„ ์ž…๋ ฅํ•œ๋‹ค.
        imageurl = data['imageurl']
        linkurl = data['linkurl']
        create_date = date_function.today()  #create_date๋Š” datetime์œผ๋กœ ์ €์žฅ
        update_date = date_function.today()

        LinkModel.save(LinkModel(None, name, tag, sort, imageurl, linkurl, create_date, update_date))

        return {'message':f'๋งํฌ ์ •๋ณด๊ฐ€ ๋“ฑ๋ก ๋˜์—ˆ์Šต๋‹ˆ๋‹ค'},201

"""
GET /api/links - ์ „์ฒด ๋งํฌ์ •๋ณด ์กฐํšŒ
"""

class LinksAll(Resource):

    def get(self):
    # ์ž…๋ ฅ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ • - ๋ชฉ๋ก์ „์ฒด ์ถœ๋ ฅ์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†์Œ.
        link = LinkModel.query.order_by(LinkModel.sort).all()
        link_schema = LinkSchema(many=True)
        output = link_schema.dump(link)
        return jsonify({'links' : output})

"""
GET /api/link/select/<int:id> - ๋งํฌ ์ •๋ณด ์กฐํšŒ
"""
class LinkSelect(Resource):

    def get(self,id):
        # ์ •๋ณด ์กฐํšŒ
        link = LinkModel.find_by_id(id)
        link_schema = LinkSchema()
        output = link_schema.dump(link)
        return jsonify({'links' : output})

"""
DELETE /api/link/delete/<int:id> - ๋งํฌ์ •๋ณด์‚ญ์ œ
"""
class LinkRemove(Resource):
    def delete(self, id):
        # ์ •๋ณด ์‚ญ์ œ ( links_model์˜ delete_by_id ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ )
        LinkModel.delete_by_id(id)

        return {'message':'์ •์ƒ์ ์œผ๋กœ ์‚ญ์ œ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'},201

"""
PUT /api/link/update/<int:id> - ๋งํฌ ์ •๋ณด ์ˆ˜์ •
"""
class LinkUpdate(Resource):

    def put(self,id):
        #Request๋ฅผ json์œผ๋กœ ๋ฐ›๋Š”๋‹ค.
        values = request.get_json()
        # ์ •๋ณด ์ˆ˜์ • ( links_model์˜ update_by_id ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ )
        LinkModel.update_by_id(id, values)
        return {'message':'์ •๋ณด๊ฐ€ ์ˆ˜์ • ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'},201

 

์ฐธ๊ณ ์ž๋ฃŒ :

Sqlalchemy

 

ORM Quick Start — SQLAlchemy 2.0 Documentation

ORM Quick Start For new users who want to quickly see what basic ORM use looks like, here’s an abbreviated form of the mappings and examples used in the SQLAlchemy Unified Tutorial. The code here is fully runnable from a clean command line. As the descri

docs.sqlalchemy.org

Flask + marshmallow for beautiful APIs 

 

Flask-Marshmallow: Flask + marshmallow for beautiful APIs — Flask-Marshmallow 0.14.0 documentation

Flask-Marshmallow: Flask + marshmallow for beautiful APIs changelog // github // pypi // issues Flask + marshmallow for beautiful APIs Flask-Marshmallow is a thin integration layer for Flask (a Python web framework) and marshmallow (an object serialization

flask-marshmallow.readthedocs.io


๋‹ค์Œ ํŽธ์—๋Š” server.py์— flask_restful์—์„œ ์ œ๊ณต๋˜๋Š” API Resource์„ ์ถ”๊ฐ€ํ•ด ๋ณด์ž.

#5. Python์œผ๋กœ API ๊ฐœ๋ฐœ

 

#5. Python์œผ๋กœ API ๊ฐœ๋ฐœ

1. Python์œผ๋กœ API ๊ฐœ๋ฐœ Python์˜ Flask์„ ์ด์šฉํ•˜์—ฌ API์ œ์ž‘์„ ํ•ด๋ณด์ž. ์šฐ์„  PostgreSQL์—์„œ DB ์„ค๊ณ„์— ๋งž๊ฒŒ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•œ๋‹ค. (DB์ƒ์„ฑ ๋ฐฉ๋ฒ•์€ ๋ณ„๋„ ์„ค๋ช…ํ•˜์ง€ ์•Š๋Š”๋‹ค.) 1.1. Python Flask APIํ™˜๊ฒฝ ์„ค์ • โŒ˜ config.py (

firstvalue.tistory.com

 

Flask๋กœ REST API ๊ตฌํ˜„ํ•˜๊ธฐ

โŒ˜ server.py ( ์œ„์น˜: linkservice/server/server.py)

---------------------------------------------------------------------------------------------------

app.py๋กœ ๋ณดํ†ต ๋ช…์นญ ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” server.py๋กœ ๋„ค์ž„์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์ง„ํ–‰ํ•œ๋‹ค.

์‹ค์ œ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํŒŒ์ผ๋กœ python3 server.py๋กœ ๊ตฌ๋™ํ•œ๋‹ค.

์šฐ์„  ์„ค์ •ํŒŒ์ผ๋“ค๋กœ ์ž˜ ๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

์•„๋ž˜๋Š” host๊ฐ€ ๋ผ์ด๋ธŒ ๋„๋ฉ”์ธ(linkserver.com) ์ด ์•„๋‹Œ ๊ฒฝ์šฐ, DEVํ™˜๊ฒฝ์œผ๋กœ ์ธ์‹ํ•œ๋‹ค.

import socket
from flask import Flask

env = socket.gethostbyname(socket.gethostname())
if 'linkserver.com' in env :
   app = create_app('production')
   print('์šด์˜๊ณ„ ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค.')
else:
    app = create_app('development')
    print(' DEV ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค.')
    
#print(app.config)

if __name__ == "__main__":
app.run(debug=True, port=5000)

์œ„ ๋‚ด์šฉ์„ ๋ชจ๋‘ ์ €์žฅํ•œ ํ›„ ํ„ฐ๋ฏธ๋„์„ ์—ด์–ด์„œ python3 server.py์„ ์‹คํ–‰ํ•ด๋ณด์ž.

 

โŽฎ์‹คํ–‰๋ฐฉ๋ฒ•

1.    ๊ฐ€์ƒํ™” ํ™˜๊ฒฝ ์‹œ์ž‘

2.    python3 server.py ์‹คํ–‰

์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค.

 

Flask ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†์–ด์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜์ธ๋ฐ, ์•ž์œผ๋กœ ์ง„ํ–‰์‹œ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ชจ๋‘ ๋ฏธ๋ฆฌ ์„ค์น˜ํ•ด๋ณด์ž

 

โŽฎLIBRARY์„ค์น˜

1.    pip install flask

2.    pip install Flask-RESTful

3.    pip install Flask-SQLAlchemy

4.    pip install marshmallow-sqlalchemy

5.    pip install flask-marshmallow

6.    pip install psycopg2

7.    pip install pillow

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ชจ๋‘ ์„ค์น˜ํ•œ ํ›„ ๋‹ค์‹œ ํ•œ๋ฒˆ ํ„ฐ๋ฏธ๋„์—์„œ python3 server.py ์„ ์‹คํ–‰ํ•œ๋‹ค.

server.py์— print(app.config) ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ํ™˜๊ฒฝํŒŒ์ผ์ด ์ž˜ ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 


๊ธฐ๋ณธ Python Flask ํ™˜๊ฒฝ์€ ๊ตฌ์„ฑ๋˜์—ˆ์œผ๋ฉฐ, ๋‹ค์ŒํŽธ์—๋Š” ๋ฐ์ดํƒ€๋ฒ ์ด์Šค ๋ชจ๋ธ ์ž‘์—…์„ ์ง„ํ–‰ํ•œ๋‹ค.

 

#6. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๋ฐ์ดํƒ€๋ฒ ์ด์Šค ๋ชจ๋ธ ์ž‘์—…)

 

#6. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๋ฐ์ดํƒ€๋ฒ ์ด์Šค ๋ชจ๋ธ ์ž‘์—…)

#5. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๊ธฐ๋ณธํ™˜๊ฒฝ๊ตฌ์„ฑ) #5. Python์œผ๋กœ API ๊ฐœ๋ฐœ(๊ธฐ๋ณธํ™˜๊ฒฝ๊ตฌ์„ฑ) โŒ˜ server.py ( ์œ„์น˜: linkservice/server/server.py) --------------------------------------------------------------------------------------------------- app.py

firstvalue.tistory.com

 

1.    Python์œผ๋กœ API ๊ฐœ๋ฐœ

Python์˜ Flask์„ ์ด์šฉํ•˜์—ฌ API์ œ์ž‘์„ ํ•ด๋ณด์ž.

์šฐ์„  PostgreSQL์—์„œ DB ์„ค๊ณ„์— ๋งž๊ฒŒ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•œ๋‹ค.

(DB์ƒ์„ฑ ๋ฐฉ๋ฒ•์€ ๋ณ„๋„ ์„ค๋ช…ํ•˜์ง€ ์•Š๋Š”๋‹ค.)

 

1.1.  Python Flask APIํ™˜๊ฒฝ ์„ค์ •

โŒ˜ config.py ( ์œ„์น˜: linkservice/server/config/config.py )

--------------------------------------------------------------------------------------------------

config.py์€ ๊ฐœ๋ฐœ๊ณ„(Development)์™€ ์šด์˜๊ณ„(Production) ํ™˜๊ฒฝ์„ค์ •์„ ์œ„ํ•œ ๊ฒƒ์œผ๋กœ ์„œ๋ฒ„(server.py) ๊ตฌ๋™ ์‹œ ํ˜ธ์ถœํ•œ๋‹ค.

 

SQLALCHEMY_DATABASE_URI์ •๋ณด๋ฅผ ์—ฌ๋Ÿฌ๋ถ„์˜ DB์ •๋ณด๋กœ ๋ณ€๊ฒฝํ•˜๊ณ , ๋‹ค๋ฅธ ์˜ต์…˜๋“ค์€ ์ฐธ๊ณ ํ•˜์—ฌ ํ•„์š”์‹œ ์ถ”๊ฐ€/์‚ญ์ œ ํ•˜๋ฉด ๋œ๋‹ค.

 

์ด๋ฏธ์ง€ ์ €์žฅ ์œ„์น˜์„ค์ •์€ CDNํ˜น์€ NASํ˜•ํƒœ๋กœ ์ œ์ž‘ํ•˜๊ณ ์ž ๋ณ„๋„ ํด๋”๋กœ ๋ถ„๋ฆฌํ–ˆ์œผ๋ฉฐ, ๋งํฌ์ •๋ณด์™€ ์ด๋ฏธ์ง€ ์ •๋ณด ์ €์žฅ์€ IMAGE_URLํ˜•ํƒœ๋กœ ์ €์žฅ๋œ๋‹ค. 

ํ™˜๊ฒฝํŒŒ์ผ ๋‚ด์šฉ์€ ์•ž์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์„ค๋ช…๋  ๊ฒƒ์ด๋‹ค

 

import os
dir = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))

class Development(object):
    DEBUG = True
    FLASK_APP = 'linkservice_dev'
    ENV = 'development'
    
## DB URL FOR DEVELOPMENT
    SQLALCHEMY_DATABASE_URI = 'postgresql://์‚ฌ์šฉ์ž:์‚ฌ์šฉ์žํŒจ์Šค์›Œ๋“œ@localhost:5432/๋ฐ์ดํƒ€๋ฒ ์ด์Šค๋ช…'

# ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์‚ฌํ•ญ์— ๋Œ€ํ•ด ์ปค๋ฐ‹ ์ „ํ›„๋กœ ์•Œ๋ฆผ ์—ฌ๋ถ€
    SQLALCHEMY_TRACK_MODIFICATIONS = True

# Query Debug ์—ฌ๋ถ€ ๋ฐ DB POOL
    SQLALCHEMY_ECHO = True
    SQLALCHEMY_POOL_SIZE = 20
    SQLALCHEMY_POOL_RECYCLE = 3600

#JSON์—์„œ ํ•œ๊ธ€ ํ‘œํ˜„์„ ์œ„ํ•ด์„œ ๋ฐ˜์˜
    JSON_AS_ASCII = False
    
#์ด๋ฏธ์ง€ ์œ„์น˜ ์„ค์ •
    image_folder = os.path.normpath(os.path.join(dir, os.pardir)) + '/client/public/images/'
    COVER_IMAGE_FORDER = image_folder
    IMAGE_FORDER = image_folder
    IMAGE_URL = 'http://127.0.0.1:3000/images/'

class Production(object):
    DEBUG = False
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ECHO = False
    FLASK_APP = 'linkservice'
    ENV = 'production'
    
## DB URL FOR PRODUCTION
    SQLALCHEMY_DATABASE_URI = 'postgresql://์‚ฌ์šฉ์ž:์‚ฌ์šฉ์žํŒจ์Šค์›Œ๋“œ@localhost:5432/๋ฐ์ดํƒ€๋ฒ ์ด์Šค๋ช…'

#JSON์—์„œ ํ•œ๊ธ€ ํ‘œํ˜„์„ ์œ„ํ•ด์„œ ๋ฐ˜์˜
    JSON_AS_ASCII = False    

#์ด๋ฏธ์ง€ ์œ„์น˜ ์„ค์ •
    image_folder = os.path.normpath(os.path.join(dir, os.pardir)) + '/images/'
    COVER_IMAGE_FORDER = image_folder
    IMAGE_FORDER = image_folder 

#์šด์˜๊ณ„ 
    IMAGE_URL = 'https://www.linkservice.com/img/'

app_config = {
    'development': Development(),
    'production': Production(),
}

โŒ˜ create_app.py ( ์œ„์น˜: linkservice/server/config/create_app.py)

---------------------------------------------------------------------------------------------------

create_app.py๋Š” ํ™˜๊ฒฝํŒŒ์ผ์„ ์ฝ์–ด Flask App์„ ์‹คํ–‰ํ•œ๋‹ค.

from . import config
from flask import Flask

def create_app(environment):
    # configํŒŒ์ผ์—์„œ ๋ถˆ๋ ค์˜ค๊ธฐ
    config_map = {
        'development': config.Development(),
        'production': config.Production(),
    }
    config_obj = config_map[environment.lower()]   
    
    app = Flask(__name__)
    app.config.from_object(config_obj)

    return app

1.1. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ ๋ฐ ํ™˜๊ฒฝ์„ค์ •

ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ๋Š” ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ๋‚˜๋ˆ„๊ณ ์ž client / server๋กœ ๊ตฌ๋ถ„ ํ•œ๋‹ค.

โŽฎํ”„๋กœ์ ํŠธ ํด๋” : linkserver/

       |-------- client ( React ๊ตฌ์กฐ)

       |-------- server ( Python๊ตฌ์กฐ )

 

โŽฎํ”„๋กœ์ ํŠธ ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ   Client ํ™˜๊ฒฝ๊ณผ Server ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•ด ๋ณด๊ฒ ๋‹ค.

ํ„ฐ๋ฏธ๋„์„ ์—ด์–ด์„œ,

     > mkdir linkservice

     > cd linkservice

 

โŽฎํ”„๋กœ์ ํŠธ ํด๋”์—์„œ react ํ™˜๊ฒฝ์œผ๋กœ client ํด๋” ์ƒ์„ฑ

     > npx create-react-app client

โŽฎํ”„๋กœ์ ํŠธ ํด๋”์—์„œ python๊ฐ€์ƒํ™˜๊ฒฝ์œผ๋กœ serverํด๋” ์ƒ์„ฑ

     > python3 -m venv server

 โŽฎํ”„๋กœ์ ํŠธ ํด๋”์ธ linkservice ์•„๋ž˜์— client์™€ serverํ™˜๊ฒฝ์ด ๊ตฌ์ถ•๋˜์—ˆ๋‹ค. 

 

 ๋‹ค์ŒํŽธ์—๋Š”  Python Flask๋กœ API ์ œ์ž‘์„ ํ•ด๋ณด์ž.

1.    ๊ฐœ๋ฐœ ์ค€๋น„

1.1. ์‚ฌ์ „ ์ค€๋น„ ์ž‘์—…

๋ฐฑ์—”๋“œ(BACK-END) : Python, Flask, SQLAlchemy์„ ํ†ตํ•ด RESTful API์ œ์ž‘,

ํ”„๋ก ํŠธ์—”๋“œ(FRONT-END) : React, Bootstrap, material ui ์„ ์‚ฌ์šฉํ•˜์—ฌ page ์ œ์ž‘

๋ฐ์ดํƒ€๋ฒ ์ด์Šค(DB) : PostgreSQL

  

1.2. ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ

์„ค์น˜๋ฐฉ๋ฒ•์€ ๋ณ„๋„ ์„ค๋ช…ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ํ•„์š”์‹œ ๊ฒ€์ƒ‰ํ•˜์‹œ๋ฉด ๋งŽ์€ ์ž๋ฃŒ๋“ค์ด ๋‚˜์™€ ์žˆ๋‹ค.

 

๐Ÿ‘€ ๋ฐฑ์—”๋“œ(BACK-END)

· Python3.6.X ( ์šด์˜์ฒด์ œ์— ๋งž๊ฒŒ ์„ค์น˜ )

๋‹ค์šด๋กœ๋“œ ์‚ฌ์ดํŠธ : https://www.python.org/downloads/

 

· POSTMAN

๋‹ค์šด๋กœ๋“œ ์‚ฌ์ดํŠธ: https://www.getpostman.com/products

 

· PostgreSQL ์ตœ์‹ ๋ฒ„์ „( ์šด์˜์ฒด์ œ์— ๋งž๊ฒŒ ์„ค์น˜ )

๋‹ค์šด๋กœ๋“œ ์‚ฌ์ดํŠธ: https://www.enterprisedb.com/downloads/postgres-postgresql-downloads

 

· DBeaver Community ๋ฒ„์ „( ์šด์˜์ฒด์ œ์— ๋งž๊ฒŒ ์„ค์น˜ )

๋‹ค์šด๋กœ๋“œ ์‚ฌ์ดํŠธ: https://dbeaver.io/download/

๏ƒ  DBeaver๋Š” DBํˆด์ด๋ฏ€๋กœ ๋‹ค๋ฅธ DBํˆด์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด ๋ณ„๋„ ์„ค์น˜ ์•ˆ ํ•ด๋„ ๋œ๋‹ค.

 

๐Ÿ‘€ ํ”„๋ก ํŠธ์—”๋“œ(FRONT-END)

· Node.js ์™€ NPM

๋‹ค์šด๋กœ๋“œ ์‚ฌ์ดํŠธ : https://nodejs.org/ko/download/

· NPX ์„ค์น˜ : ๋ช…๋ น ํ”„๋กฌํ”„ํŠธ์—์„œ ์‹คํ–‰> npm install npx -g

· Yarn ์„ค์น˜ : ๋ช…๋ น ํ”„๋กฌํ”„ํŠธ์—์„œ ์‹คํ–‰> npm install yarn -g

1. ๊ธฐ๋ณธ ์„ค๊ณ„

ํ™”๋ฉด
ํ™”๋ฉด ๋‚ด์šฉ๊ณผ DB ๋ฐ API ์ •์˜

๊ฐ„๋‹จํ•œ ํ”„๋กœ๊ทธ๋žจ์ด์ง€๋งŒ, ํ”„๋กœ์ ํŠธ ๋งค๋‹ˆ์ €๋‚˜ ๊ธฐํš์ž์ฒ˜๋Ÿผ, ๊ฐœ๋ฐœ ์ด์ „์— ์–ด๋Š์ •๋„ ์„ค๊ณ„๊ฐ€ ๋˜์–ด์•ผ ๊ฐœ๋ฐœ์˜ ์ผ๊ด€์„ฑ์ด ์ƒ๊ธด๋‹ค.

ํ˜ผ์ž์„œ ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๊ฒŒ ๋˜์–ด ์‹ค์ œ ๋‚ด์šฉ์ด ์–ด์„คํ”„๋”๋ผ๋„ ์–‘ํ•ดํ•ด์ฃผ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

ํ™”๋ฉด์€ ๋””์ž์ธ์ด ๋˜์—ˆ์ง€๋งŒ, ์‚ฌ์‹ค ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ํผ๋ธ”๋ฆฌ์‹ฑ์„ ํ–ˆ๊ณ , ๊ทธ ๋‹จ๊ณ„ ๋‹จ๊ณ„๋ฅผ ์•ž์œผ๋กœ ๋ณด์—ฌ์ค„ ์˜ˆ์ •์ด๋‹ค.

 

IT๊ธฐ์—…์„ ๋‹ค๋‹ ๋•Œ๋Š” ์ด ๋ชจ๋“  ๊ฒƒ์„ ์‚ฐ์ถœ๋ฌผ ํ˜•ํƒœ๋กœ ํŒŒ์›Œํฌ์ธํŠธ์™€ ์—‘์…€๋กœ ์ž‘์—…์„ ํ–ˆ์ง€๋งŒ, ์š”์ฆ˜์€ ์Šค์ผ€์น˜(Sketch), ํ”ผ๊ทธ๋งˆ(Figma), XD์™€ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค ๋””์ž์ธ์„ ์œ„ํ•œ ํ˜‘์—… ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋“ฑ์ด ๋งŽ์ง€๋งŒ, ๊ธฐ ์‚ฌ์šฉํ•ด๋ณธ Figma์„ ํ†ตํ•ด ๊ฐ„๋‹จํžˆ UI์„ค๊ณ„๋ฅผ ์ง„ํ–‰ ํ–ˆ๋‹ค. UI๋ถ€๋ฌธ๋„ ๋ณดํ†ต ๊ธฐ์—…์—์„œ๋Š” ๋””์ž์ธํŒ€์—์„œ ์ œ์ž‘ํ•˜์—ฌ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์‹œ์— ๋ถ€๋‹ด์ด ๋œ ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, ๋””์ž์ธ์ด ์•ฝํ•œ ๋‚˜๋Š” CSS์—์„œ ๋งŽ์€ ์‹œ๊ฐ„์„ ํ—ˆ๋น„ํ–ˆ๋‹ค.

์—ฌ๋Ÿฌ ์‚ฌ์ดํŠธ์˜ CSS์„ ์ฐธ๊ณ ํ•˜์—ฌ ๋‚˜๋ฆ„๋Œ€๋กœ ๋งŒ๋“  ๊ฒƒ์œผ๋กœ, ํผ๋ธ”๋ฆฌ์‹ฑ์— ๋Šฅํ•œ ๋ถ„์ด๋ผ๊ณ  ๋‚˜๋ฆ„ ์ˆ˜์ •ํ•˜์…”์„œ ์‚ฌ์šฉํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค

 

1.1. DB ์„ค๊ณ„

UI ์„ค๊ณ„๋œ ๊ธฐ์ค€์œผ๋กœ DB ์„ค๊ณ„๋ฅผ ํ•ด๋ณด์ž. ๋˜ํ•œ ๊ธฐ๋ณธ์ ์ธ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ํ…Œ์ด๋ธ”๋„ ๊ตฌ์„ฑํ•œ๋‹ค.

  1) Link์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ํ…Œ์ด๋ธ” ( links )

  2) ์›Œ๋“œ ํด๋ผ์šฐ๋“œ๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ด๋ธ”( tagcloud )

       โ–ท ์›Œํฌ ํด๋ผ์šฐ๋“œ๋Š” ๋ณ„๋„ ๊ตฌํ˜„ ์•ˆ ํ•ด๋„ ๋˜๋ฉฐ, ํ–ฅํ›„ ํ•„์š”์‹œ ์†Œ์Šค๋งŒ ์ฐธ๊ณ ํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.

3) ์ผ์ •์„ ๋“ฑ๋ก( ํ–‰์‚ฌ์˜คํ”ˆ์ผ์ • ๋“ฑ )์„ ์œ„ํ•œ ํ…Œ์ด๋ธ” ( plans )

       โ–ท ํ™”๋ฉด ์ปค๋ฒ„ ์ด๋ฏธ์ง€์— ์ผ์ •์ด ํ‘œ์‹œ๋  ์˜ˆ์ •์ด๋‹ค.

4) ๋กœ๊ทธ์ธ์„ ์œ„ํ•œ ์œ ์ €์ •๋ณด ํ…Œ์ด๋ธ” ( user )

 

1.2. API์ •์˜

๊ฐ DB ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ CRUD์„ API๋กœ ์ •์˜ํ•˜๊ณ , ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ, ํšŒ์›๊ฐ€์ž…์ฒ˜๋ฆฌ, ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ API์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

 

๋ชจ๋“  API์„ ๋ฏธ๋ฆฌ ๊ธฐ๋กํ•ด๋‘๊ณ  ์‹ถ์ง€๋งŒ, ์„ค๋ช…์„ ์œ„ํ•ด์„œ ์—ฌ๊ธฐ์„œ ์ •์˜๋˜์ง€ ์•Š์€ API ๋Š” ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

 


๋‹ค์ŒํŽธ์— ๊ณ„์†........

 

1.    ๊ฐœ๋ฐœ ๊ฐœ์š”

 

IT๊ธฐ์—…์„ ํ‡ด์‚ฌ ํ›„ ๊ฐœ๋ฐœ ๊ณต๋ถ€๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด์„œ ์–ด๋–ค ํ”„๋ ˆ์ž„์›Œํฌ/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ฐฐ์šธ๊นŒ ๊ณ ๋ฏผํ•˜๋ฉด์„œ ์˜ˆ์ „์— ์ž ๊น ๊ณต๋ถ€ํ–ˆ๋˜ Angularjs์„ ๋‹ค์‹œ ํ•ด๋ณผ๊นŒ ํ–ˆ์œผ๋‚˜, ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ฐฐ์šฐ๊ธฐ๋กœ ๋งˆ์Œ ๋จน์œผ๋ฉด์„œReact์™€ Python ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ–ˆ๋‹ค.

 

๋‹ค๋‹ˆ๋˜ ํšŒ์‚ฌ๋Š” JAVA์˜ ์œ„์ฃผ์˜ MSA, ์ž์ฒด ํ”„๋ ˆ์ž„์›Œํฌ ์†”๋ฅ˜์…˜๋“ฑ ์‚ฌ์šฉํ•˜์—ฌ, ์ตœ์‹  ํ”„๋ ˆ์ž„์›Œํฌ/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ฐฐ์šธ ์‹œ๊ฐ„๋„ ์—†์—ˆ๊ณ , ๊ณผ์žฅ๊ธ‰ ์ดํ›„ ๋ถ€ํ„ฐ๋Š” ๊ฐœ๋ฐœ๋ณด๋‹ค๋Š” BA ์—ญํ• ๋งŒ ํ•˜๋‹ค ๋ณด๋‹ˆ, ํ”„๋ ˆ์ž„์›Œํฌ/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ƒˆ๋กญ๊ฒŒ ๊ณต๋ถ€ํ•˜๋Š”๊ฒƒ์ด ์–ด๋ ค์›€์ด ์žˆ์—ˆ๋‹ค.

 

๋ณธ ๋‚ด์šฉ์€ ์•ฝ 2๋‹ฌ๊ฐ„ React์™€ Python์„ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๊ตฌ์ถ•ํ•œ ๋‚ด์šฉ์˜ ์†Œ์Šค๋ฅผ ๊ฐœ๋ฐœ ๋‹จ๊ณ„๋ถ€ํ„ฐ ํ•˜๋‚˜ํ•˜๋‚˜ ๋”ฐ๋ผ ํ• ์ˆ˜ ์žˆ๋„๋ก ๊ณต์œ ํ•˜์—ฌ ๋งŽ์€ ๋ถ„๋“ค๊ณผ ๋‚˜๋ˆ„๊ณ ์ž ํ•œ๋‹ค.

 

๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๋‹ค๋ณด๋‹ˆ, ์ดˆ๋ณด ํ˜น์€ ์ฒซ ํ”„๋กœ์ ํŠธ ์‹ ์ž…์€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ ๋‹ค.

์†Œ์Šค ์œ„์ฃผ์˜ ๋‚ด์šฉ์ด๋ฏ€๋กœ, ์†Œ์Šค๋‚ด์— ์ฃผ์„์ฒ˜๋ฆฌ๋ฅผ ํ–ˆ๋‹ค๊ณ  ํ–ˆ์œผ๋‚˜, ๋งŽ์ด ๋ถ€์กฑํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…์€ ์•Œ๊ณ  ์žˆ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ์ง„ํ–‰๋˜๋ฏ€๋กœ, ํ•„์š”์‹œ ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์ถ”๊ฐ€์ ์œผ๋กœ ๊ณต๋ถ€๋ฅผ ํ•˜์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

 

๊ฐœ๋ฐœํ•  ๋•Œ ๊ฐœ๋…์ ์ธ ์„ค๋ช…๋งŒ ํ•˜๋Š” ์ž๋ฃŒ๊ฐ€ ๋งŽ์•„, ์‹ค์ œ ์šด์˜๋˜๋Š” ์ฐธ๊ณ ์†Œ์Šค๋ฅผ ์ฐพ๊ธฐ๊ฐ€ ์–ด๋ ค์šด ๊ฒฝํ—˜์ด ์žˆ์—ˆ๊ณ , ์ด๋Ÿฐ ๊ฒฝํ—˜์„ ์ดˆ๋ณด ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ๋„์›€์„ ์ฃผ๊ณ ์ž ๋ชจ๋“  ์†Œ์Šค๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š”๊ฒƒ์„ ๋”ฐ๋ผํ•˜๋ฉด์„œ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์ตํžˆ๊ธฐ ๋ฐ”๋ผ๋Š” ๋งˆ์Œ์— ์•ž์œผ๋กœ ๊ณ„์† ์—ฌ๊ธฐ์— ๊ธฐ๋ก์„ ๋‚จ๊ธฐ๋ฉด์„œ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ด๋‹ค.

 

   โ€ป ์ตœ์ข… ์†Œ์Šค๋Š” ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„  or ์ค‘๊ฐ„๋‹จ๊ณ„์— git์œผ๋กœ ๊ณต์œ ํ•  ์˜ˆ์ •์ด๋‹ค.

 

1.1. ๋ฌด์—‡์„ ๋งŒ๋“ค ๊ฒƒ์ธ๊ฐ€?

๊ฐœ๋ฐœ ๊ณต๋ถ€๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด์„œ ๋ฌด์ž‘์ • ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค ์–ด๋–ค ๊ฒƒ์„ ๋งŒ๋“ค์ง€ ๋ชฉํ‘œ๋ฅผ ์ •ํ•ด์„œ ๊ณต๋ถ€๋ฅผ ํ•˜๋ฉด ์ข‹์„ ๋“ฏ ํ•˜๋‹ค.

 

์š”์ฆ˜์€ ๋งŽ์€ ์ธํ”Œ๋ฃจ์–ธ์Šค๋‚˜ ์œ ํˆฌ๋ฒ„ ๋“ฑ ํฌ๋ฆฌ์—์ดํ„ฐ ๋ถ„๋“ค์ด ๋‹ค์–‘ํ•œ ์ฝ˜ํ…์ธ ๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๋ฉ€ํ‹ฐ๋งํฌ ์‚ฌ์ดํŠธ๋“ค์ด ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

๋ฉ€ํ‹ฐ ๋งํฌ ์„œ๋น„์Šค๋Š” ์ธ์Šคํƒ€๊ทธ๋žจ, ๋ธ”๋กœ๊ทธ, ๋งˆ์ผ“ ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋งํฌ๋ฅผ ํ•œ๊ณณ์— ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

 

     ์˜ˆ) ๋งํฌํŠธ๋ฆฌ(Linktree), ์ธํฌํฌ๋งํฌ(Inpock link), ๋งํฌ๋ฏน์Šค(Link Mix), ๋ฆฌํ‹€๋ฆฌ(litt.ly), ๋งํฌ์˜จ(Linkon), ๋ฆฌํŠธ๋งํฌ(Lit.Link) ๋“ฑ๋“ฑ

           ์•ฝ 20์—ฌ๊ฐœ๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์•Œ๊ณ  ์žˆ๋‹ค.

 

๊ฐ„๋‹จํ•œ ์„œ๋น„์Šค์ธ ๋งŒํผ ์‚ฌ์—… ๋ชฉ์ ์œผ๋กœ ๊ฐœ๋ฐœ์ด ์•„๋‹ˆ๋ฏ€๋กœ, ๋ฉ€ํ‹ฐ์œ ์ €๋‚˜ ๋ฉ€ํ‹ฐ์ปดํผ๋‹ˆ ๊ฐ™์€ ์„œ๋น„์Šค๋Š” ์ œ์™ธํ•˜๊ณ , ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๊ณผ ์‚ฌ์ดํŠธ์— ์šด์˜์— ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ๊ณจ๋ผ ๋งํฌ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณด๊ณ ์ž ํ•œ๋‹ค.

 


๋‹ค์ŒํŽธ์€ ๊ฐ„๋‹จํ•œ ์„ค๊ณ„๋ฅผ ์ง„ํ–‰ํ•˜๊ฒ ๋‹ค.

 

โ€ป ๋ณธ ํฌ์ŠคํŒ…์€ ๊ณ„์† ์ˆ˜์ •๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

+ Recent posts