Flask ๋ฐ Docker ์‹œ์ž‘๐Ÿณ๐Ÿš€

26645 ๋‹จ์–ด tutorialpythonbeginnersdocker
์ง€๋‚œ ๋ช‡ ์ฃผ ๋™์•ˆ, ๋‚˜๋Š” ๊ฐ์ข… ์šฉ๋ก€์—์„œflask ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐœ๋ฐœํ–ˆ๋‹ค.๋ชฉ์ ์€ ๋‚ด๊ฐ€ ์†Œ๋ณ‘์— ๋Œ€ํ•œ ์ง€์‹๊ณผ ์ƒ์‚ฐ ์‘์šฉ์˜ ์ •ํ™•ํ•œ ๊ตฌ์กฐ๋ฅผ ๋ณต์Šตํ•˜๋Š” ๊ฒƒ์ด๋‹ค.์ž…๋ฌธ ํ”„๋กœ์ ํŠธ์— docker์™€flask ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•˜๊ณ  ์ ์–ด ๋‹ฌ๋ผ๋Š” ์š”์ฒญ์„ ๋ฐ›์•˜์„ ๋•Œ์ด๊ฒƒ์€ ๋‚˜์˜ ์ง€์‹์„ ์ง„์ •์œผ๋กœ ๊ณต๊ณ ํžˆ ํ•˜๊ณ , ๋‚ด ๋ฒ„์ „์˜ ๋น ๋ฅธ ์ž…๋ฌธ ์•ˆ๋‚ด์„œ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์™„๋ฒฝํ•œ ๊ธฐํšŒ์ด๋‹ค.

๋Œ€์ƒ ๋ฐ ๋ชฉํ‘œ


๋ณธ๊ณ ๋Š” ์ดˆ๋ณด์ž ๊ฐœ๋ฐœ์ž๋ฅผ ๋Œ€์ƒ์œผ๋กœ docker์™€flask๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์•ˆ๋‚ด์„œ๋ฅผ ์ฐพ๊ณ  ์žˆ๋‹ค.๊ทธ๋Ÿฌ๋‚˜ ๊ฐ€์šด๋ฐ
๊ฐœ๋ฐœ์ž๋Š” ๋˜ ์•ฝ๊ฐ„์˜ ์ง€์‹์„ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ๋‹ค.๋‚˜๋Š” ๋˜ ๋‚ด๊ฐ€ ์ด ํ”„๋กœ์ ํŠธ์—์„œ ์ง๋ฉดํ•œ ๋ฌธ์ œ๋ฅผ ์ง€์ ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•  ๊ฒƒ์ด๋‹ค.
๋ณธ๊ณ ๋Š” ๊ฐ„๋‹จํ•œ flask ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐœ๋ฐœํ•˜์—ฌ ์ด ํ”„๋กœ๊ทธ๋žจ์„ ์—ฐ๊ฒฐํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ GitHub๋กœ ์ „์†กํ•˜๋Š” ๋ฐ ๋ชฉ์ ์„ ๋‘๊ณ  ์žˆ๋‹ค.

์„ ๊ฒฐ ์กฐ๊ฑด


๋ณธ๋ฌธ๊ณผ ํ›„์† ์ฝ”๋“œ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๋”ฐ๋ฅด๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ ๊ฒฐ ์กฐ๊ฑด์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ํŒŒ์ดํ†ค๊ณผ pip(ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ ๋ฒ„์ „์€ 3.9.7) 3.7 ์ด์ƒ์ด๋ฉด ์–ด๋–ค ๋ฒ„์ „์ด๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Git๊ฐ€ ์‹œ์Šคํ…œ์— ์„ค์น˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์‹œ์Šคํ…œ์˜
  • DockerInstallation instructions
  • ํ„ฐ๋ฏธ๋„.
  • ์ดˆ๊ธฐ ์„ค์ •


    ์ด๋Ÿฌํ•œ ๋ช…๋ น์€ ๋Œ€๋ถ€๋ถ„์˜ Unix ์‹œ์Šคํ…œ์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.์ฐธ๊ณ : Windows ๊ตฌํ˜„์€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์—์„œ ์ƒˆ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์ด ๋””๋ ‰ํ† ๋ฆฌ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
  • mkdir flask_starter_app && cd flask_starter_app
    
  • ์€ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๊ฐ€์ƒ ํ™˜๊ฒฝ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.๋˜๋Š” ์›ํ•˜๋Š” ๊ฐ€์ƒ ํ™˜๊ฒฝ์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ์€ pip๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” pip์— ํ•„์š”ํ•œ ๋ชจ๋“ˆ์„ ๊ณ„์† ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.flask, flask bootstrap, jikanpy
  • ์„ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • ์€ ์„ค์น˜๋œ ํŒจํ‚ค์ง€๋ฅผ ์š”๊ตฌ์‚ฌํ•ญ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  •   python3 -m venv venv
      source venv/bin/activate
      pip install flask flask-bootstrap jikanpy 
      pip freeze > requirements.txt
    
    ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•ด ๋ฉ”์ธ ํ”Œ๋ผ์Šคํฌ ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•˜๊ณ  ์žˆ๋‹ค.Flask Bootstrap์€ Bootstrap์„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ํ†ตํ•ฉํ•˜์—ฌ ์Šคํƒ€์ผ๋งํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    Jikanpy๋„ ์„ค์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค. Jikan Api์˜python ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค. ๋น„๊ณต์‹์ ์ธ MyAnimeList Api์ž…๋‹ˆ๋‹ค.
    ๋ชจ๋“  ๊ฒƒ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์„ค์น˜๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.๋˜๋Š” ์—์„œ ์ฝ”๋“œ ๋ณด๊ธฐ

    ์บ„๋ณด๋ผ 1 / flask starter ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ


    docker๋ฅผ ์ด์šฉํ•œ ๊ฐ„๋‹จํ•œ ํ”Œ๋ผ์Šคํฌ ์ž…๋ฌธ ํ”„๋กœ๊ทธ๋žจ


    flask starter ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ


    ๊ฐ„๋‹จํ•œ ํ”Œ๋ผ์Šคํฌ ์ž…๋ฌธ ํ”„๋กœ๊ทธ๋žจ, docker๋ฅผ ์ด์šฉํ•˜์—ฌ jikanpy์œผ๋กœ ๊ณ„์ ˆ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ „์‹œ
    (myanimelist ๋น„๊ณต์‹api).

    ๋ฌธ์žฅ.


    ์ž‘์„ฑ์— ๋งํฌ

    Docker ๋น ๋ฅธ ์‹œ์ž‘


    ํ˜ธํ™˜๋˜๋Š” ๋ฒ„์ „์˜ Python๊ณผ Node๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก Docker๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.
    ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ Dockerfile์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฐœ๋ฐœ ๋ฒ„์ „์„ ๊ตฌ์ถ•ํ•˜๋‹ค
    docker build -t flask-starter-app .   

    To run the app

     docker run --name=flask-app -p 5001:5000 -t -i flask-starter-app  

    If everything went well, the app should be running on localhost:5001




    ๋‹ค ์ปจํ…Œ์ด๋„ˆ์˜ˆ์š”.

    Docker refers to open source containerization platform.
    Containers are standardized executable components that combine application source code with OS-level dependencies and libraries. We can create containers without docker, however it provides a consistent, simpler and safer way to build containers. One of the major reasons for the meteoric growth of the use of containers from software development to software delivery and even testing, is the ease of use and reproducibility of the entire workflow.

    Previously developers used Virtual Machines in the cloud or self-hosted servers to run their applications and workloads.
    However, going from development to production was sometimes plagued with failures due differences in Operating systems or
    at times dependencies. Containers allow us to essentially take the code, file structure, dependencies etc. and package them and deploy them to a server and have them run as expected with minimal changes.

    ์šฉ์–ด

    Here we'll run through some tools and terminology in reference to Docker:

    DockerFile

    Docker containers start out as single text file containing all the relevant instructions on how build an image.
    A Dockerfile automates the process of creating an image, contains a series of CLI instructions for the Docker engine to assemble the image.

    Docker ์ด๋ฏธ์ง€

    Docker images hold all application source code, libraries and dependencies to run an application. It is very possible to build a docker image from scratch but developers leverage common repositories to pull down pre-built images for common software and tools.
    Docker images are made up of layers and each time a container is built from an image. a new layer is added becoming the latest version of the image. You can use a single to run multiple live containers.

    ๋ถ€๋‘ ์„ผํ„ฐ

    This is a public repository of Docker images, containing over 100,000 container images. It holds containers of software from commercial vendors, open-source projects and even individual developers.

    Docker ๋ฐ๋ชฌ

    Refers the service that runs in your system powering the creation of Docker images and containers.The daemon receives commands from client and executes them.

    Docker ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ

    This is an open-source scalable storage and distribution system for docker images. Using git( a version control system) the registry track image versions in repositories using tags for identification.

    ๊ฑด์„คํ•ฉ์‹œ๋‹ค!

    ๋ณ‘.

    Flask prides itself in being micro framework therefore it only comes with
    simple configuration out of the box. However. it allows for a wide range of custom configuration options. This gives you
    the freedom to start simple, add extensions for variety utilities as you grow.

    ์šฐ๋ฆฌ ๋ญ ์ง“๊ณ  ์žˆ์–ด?

    Today we'll be building a simple web app to display the current seasonal anime from MyAnimeList. If you follow me on you'll know am a massive manga and anime fan. MyAnimeList is defacto platform for information, reviews and rankings thus it was the optimal choice. However, it doesn't have an API or sdk to access their content. Normally we would have to scrape the site, luckily the awesome community created Jikan Api as well jikanpy which is python wrapper for the API.

    ๋น„๋ฐ€๋ฒˆํ˜ธ ์–ด๋”” ์žˆ์–ด์š”?

    Now hopefully you carried out the steps above in Prerequisites section. Ensure your virtual environment is activated. Inside our flask-starter-app directory create run.py file.

    # run.py
    from app import app
    
    
    if __name__ == '__main__':
        app.run()
    
    
    ์ด ํŒŒ์ผ์€ ์ €ํฌ ํ”„๋กœ๊ทธ๋žจ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.์šฐ์„ , ์šฐ๋ฆฌ๋Š” app ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ์šฐ๋ฆฌ์˜ app ์‹ค๋ก€๋ฅผ ๊ฐ€์ ธ์™”๋Š”๋ฐ, ์ด ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ์•„์ง ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค.์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ์ฐฝ์กฐํ•ฉ์‹œ๋‹ค.
    ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์—ฌ๊ธฐ์„œ ๋งŒ๋“ค๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜์‹ญ์‹œ์˜ค.
  • ํ…œํ”Œ๋ฆฟ ํด๋”
  • __init__.py ํŒŒ์ผ
  • models.py ํŒŒ์ผ
  • views.py ํŒŒ์ผ
  • anime_requests.py ํŒŒ์ผ
  • ์ด์ œ ํด๋” ๊ตฌ์กฐ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    python-projects $ tree flask_starter_app
    flask_starter_app
    โ”œโ”€โ”€ app
    โ”‚   โ”œโ”€โ”€ __init__.py
    โ”‚   โ”œโ”€โ”€ anime_request.py
    โ”‚   โ”œโ”€โ”€ models.py
    โ”‚   โ”œโ”€โ”€ views.py
    โ”‚   โ””โ”€โ”€ templates
    โ”‚       โ””โ”€โ”€ index.html
    โ””โ”€โ”€ requirements.txt
    โ””โ”€โ”€ run.py
    โ””โ”€โ”€ venv
    
    
    2 directories, 7 files
    
    __init__.py ํŒŒ์ผ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    # app/__init__.py
    
    from flask import Flask
    from flask_bootstrap import Bootstrap
    
    bootstrap = Bootstrap()
    app = Flask(__name__)
    bootstrap.init_app(app)
    from . import views
    
    
    ์•ž์˜ ๋‘ ํ–‰์€ ๊ฐ ๋ชจ๋“ˆ์—์„œ Flask ๋ฐ Bootstrap ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.๊ทธ๋Ÿฐ ๋‹ค์Œ Bootstrap ํด๋ž˜์Šค๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๊ณ  Bootstrap ๋ณ€์ˆ˜๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.app ๋ณ€์ˆ˜๋Š” Flask ํด๋ž˜์Šค์˜ ์‹ค๋ก€๋ฅผ ํฌํ•จํ•˜๊ณ  ์ €ํฌ app๋ฅผ ์ธ์šฉํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ด๋ฆ„์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” init_app() ๋ฐฉ๋ฒ•์„ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ purapp๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์•ˆ๋‚ด๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
    ๋งˆ์ง€๋ง‰์œผ๋กœ ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ๋ณด๊ธฐ ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    ์ˆ˜์—…์„ ์‹œ์ž‘ํ•˜๋‹ค

    models.py ํŒŒ์ผad์—์„œ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
    # app/models.py
    from dataclasses import dataclass
    
    @dataclass
    class Anime:
        """
        class to model anime data
        """
        mal_id: int
        url: str
        title: str
        image_url: str
        synopsis: str
        type: str
        airing_start: str
        episodes: int
        members: int
    
    ์ด ํŒŒ์ผ์€ Api์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ชจ๋“  ๋ชจ๋ธ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” dataclasses ๋ชจ๋“ˆ์—์„œ ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค ์žฅ์‹๊ธฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.์ด๊ฒƒ์€ ์šฐ๋ฆฌ๋กœ ํ•˜์—ฌ๊ธˆ ๊ฐ์ข… ํŠน์ˆ˜ํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ , ์šฐ๋ฆฌ์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์œ ์ง€ํ•  ๊ฒƒ์ด๋‹ค.decorator๋ฅผ ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ Api์—์„œ ๋ฐ์ดํ„ฐ์˜ ๊ตฌ์กฐ๋ฅผ ๊ณ„์† ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.์ž์„ธํ•œ ๋‚ด์šฉ์€ docs์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

    ๋ถ€ํƒ

    anime_request.py ํŒŒ์ผ์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    # app/anime_request.py
    from jikanpy import Jikan
    from .models import Anime
    
    jikan = Jikan()
    # function to get seasonal anime
    def get_season_anime():
        """
        function to get the top anime from My anime list
        :return: list of anime
        """
        season_anime_request = jikan.season()
        season_anime = []
        if season_anime_request['anime']:
            response = season_anime_request['anime']
    
            for anime in response:
                mal_id = anime.get('mal_id')
                url = anime.get('url')
                title = anime.get('title')
                image_url = anime.get('image_url')
                synopsis = anime.get('synopsis')
                type = anime.get('type')
                airing_start = anime.get('airing_start')
                episodes = anime.get('episodes')
                members = anime.get('members')
    
                new_anime = Anime(mal_id, url, title, image_url, synopsis, type, airing_start, episodes, members)
                season_anime.append(new_anime)
    
        return season_anime
    
    ์œ„ ์ฝ”๋“œ์—์„œ Jikanpy ๋ชจ๋“ˆ์—์„œ Jikan ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค. ์ด๋กœ์จ Jikan Api๋ฅผ ์š”์ฒญํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” ๋˜ํ•œ models ํŒŒ์ผ์—์„œ ์šฐ๋ฆฌ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ข…๋ฅ˜๋ฅผ ๊ฐ€์ ธ์™”๋‹ค.์šฐ๋ฆฌ๋Š” ๋ณ€์ˆ˜ jikan์„ ๋งŒ๋“ค๊ณ , ๊ทธ ๋ณ€์ˆ˜์— jikan ํด๋ž˜์Šค๋ฅผ ๋ถ„๋ฐฐํ•˜๋Š” ์‹ค๋ก€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    ์ด์ œ get_season_anime ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜์—ฌ Jikan Api์— ์š”์ฒญํ•˜์—ฌ ๋ชฉ๋ก์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” ๋ณ€์ˆ˜ season_anime_request์„ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, ์ด ๋ณ€์ˆ˜๋Š” Jikan๋ฅ˜์—์„œ season ๋ฐฉ๋ฒ•์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.์ด๊ฒƒ์€ ๋‘ ๊ฐ€์ง€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์•„๋“ค์ธ๋‹ค. ์—ฐ๋„์™€ ๊ณ„์ ˆ์ด๋‹ค. ์—ฐ๋„, ์‹ฌ์ง€์–ด ๊ณ„์ ˆ์—์„œ ํŠน์ •ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด ๋งค์šฐ ํŽธ๋ฆฌํ•˜๋‹ค.์šฐ๋ฆฌ์˜ ์˜ˆ์—์„œ ์šฐ๋ฆฌ๋Š” ์ด๋ฒˆ ์‹œ์ฆŒ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์–ป๊ธฐ ์œ„ํ•ด ์ง€์ •ํ•˜์ง€ ์•Š์•˜๋‹ค.๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ๋นˆ ๋ชฉ๋ก์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    season ๋ฐฉ๋ฒ•์€ ๊ฐ์ข… ํ‚ค ๊ฐ’์ด ๋งž๋Š” ์‚ฌ์ „์„ ๋˜๋Œ๋ ค์ค๋‹ˆ๋‹ค.์šฐ๋ฆฌ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” animeํ‚ค์˜ ๊ฐ’์ด๋‹ค.์ด๊ฒƒ์€ ์‚ฌ์ „ ๋ชฉ๋ก์ด๋‹ค.์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ํ‚ค๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ดif๋ฌธ์žฅ์„ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ์ด ๊ฐ’์„ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” ์‘๋‹ต์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ธ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ ๋‹นํ•œ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    Anime ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์ธ new_anime ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” ํด๋ž˜์Šค๋ฅผ ๋นˆ ๋ชฉ๋ก์— ์ถ”๊ฐ€ํ•˜๊ณ , ๋งˆ์ง€๋ง‰์œผ๋กœ ํด๋ž˜์Šค ๋ชฉ๋ก์„ ๋˜๋Œ๋ ค์ค๋‹ˆ๋‹ค.

    ๋ฉฐ์น  ํ’๊ฒฝ

    views.py ํŒŒ์ผ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    from flask import render_template
    from .anime_request import get_season_anime
    
    from . import app
    
    
    @app.route('/', methods=['GET', 'POST'])
    def index():
        """
        root page view that returns the index page and its data
        :return: index template
        """
        season_anime = get_season_anime()
        return render_template('index.html', season_anime=season_anime)
    
    
    ์ด ํŒŒ์ผ์€ flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.ํ˜„์žฌ, ์šฐ๋ฆฌ๋Š” ํ•˜๋‚˜๋ฟ์ž…๋‹ˆ๋‹ค. ๋งˆ์Œ๋Œ€๋กœ ๋” ๋งŽ์ด ์ถ”๊ฐ€ํ•˜์„ธ์š”.์šฐ๋ฆฌ๋Š” ์šฐ์„  render_template์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ html ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ํ•„์š”ํ•œ ๋ชจ๋“  ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.์• ๋‹ˆ๋ฉ”์ด์…˜ ์š”์ฒญ ํŒŒ์ผ์—์„œ get_season_anime ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.๋˜ํ•œ __init__.py ํŒŒ์ผ์—์„œ ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ณต๊ฐœ ๋ฃจํŠธ ๋ฐฉ๋ฒ•์˜ @app ์žฅ์‹๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.์ด๊ฒƒ์€ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋˜๋Š” ๋ฃจํŠธ์™€ ๋ฃจํŠธ๊ฐ€ ํ—ˆ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.
    ์šฐ๋ฆฌ๋Š” index ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ–ˆ๋Š”๋ฐ, ์‚ฌ์šฉ์ž๊ฐ€ ๋ฃจํŠธ๋ฅผ ์—ด๋ฉด ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.ํ•จ์ˆ˜์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํด๋ž˜์Šค์˜ ์‹ค๋ก€ ๋ชฉ๋ก์„ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” ์ตœ์ข…์ ์œผ๋กœ render_template ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ƒ‰์ธ์„ ์ „๋‹ฌํ–ˆ๋‹ค.ํ…œํ”Œ๋ฆฟ ํด๋”์˜ html ํŒŒ์ผ๊ณผ ํ…œํ”Œ๋ฆฟ์˜ ๊ณ„์ ˆ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.
    ๋‹ค์Œ ๋‚ด์šฉ์„ ์ƒ‰์ธ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.ํ…œํ”Œ๋ฆฟ ํด๋”์˜ html ํŒŒ์ผ:
    <!--app/templates/index.html -->
    {% extends 'bootstrap/base.html' %}
    
    {% block navbar %}
        <div class="navbar navbar-inverse" role="navigation">
            <div class="container-fluid">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="/"> Anime Watchlist </a>
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li><a href="/">Home</a></li>
                    </ul>
                </div>
            </div>
        </div>
    {% endblock %}
    
    {% block content %}
        {% for anime in season_anime %}
            <div class="card-group col-xs-12 col-sm-4 col-md-2 col-lg-2">
                <div class="card">
                    <img src="{{ anime.image_url }}" alt="{{ anime.title }} poster" class="img-responsive"
                         style="max-height: 30rem">
                    <li class="text-left ">
                        <a href="/anime/{{anime.mal_id}}">
                            {{ anime.title|truncate(30)}}</a>
                        <p> Episodes : {{ anime.episodes }}</p>
                        <p> date started airing: {{ anime.airing_start | truncate(13) }}</p>
                    </li>
                </div>
    
            </div>
        {% endfor %}
    {% endblock %}
    
    Flask๋Š” jinja ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.์ด๊ฒƒ์€ ์šฐ๋ฆฌ๋กœ ํ•˜์—ฌ๊ธˆ ์ผ๋ จ์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.์šฐ๋ฆฌ์˜ ์˜ˆ์—์„œ ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ์•ˆ๋‚ด ์Šคํƒ€์ผ์„ ํฌํ•จํ•˜๋Š” ๊ธฐ๋ณธ html ํŒŒ์ผ์„ ํ™•์žฅํ–ˆ๋Š”๋ฐ ์ด๊ฒƒ์€ ๋ชจ๋“  ํŽ˜์ด์ง€์— ์ ์šฉ๋˜๋Š” ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค.์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ํŠน์ˆ˜ํ•œ {% %}์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ˆ˜ํ•œ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ธ”๋ก์„ ์ •์˜ํ•œ๋‹ค.
    ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ, ์ด๊ฒƒ์€ ์ž์‹ ์˜ ํŒŒ์ผ์— ์„ค์ •๋˜์–ด ๊ฐ€์ ธ์˜จ ๊ฒƒ์ด์ง€๋งŒ, ์—ฌ๊ธฐ์—๋Š” ๊ทธ๊ฒƒ์„ ์—ฌ๊ธฐ์— ๋†“์„ ๊ฒƒ์ด๋‹ค.์šฐ๋ฆฌ๋Š” ๋ณด๊ธฐ ํŒŒ์ผ์—์„œ ์ „๋‹ฌ๋œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ˆœํ™˜ํ•˜๋Š” ๋‚ด์šฉ ๋ธ”๋ก์„ ์ •์˜ํ–ˆ๋‹ค.๋ชจ๋“  ๊ฐ’์— ๋Œ€ํ•ด ์šฐ๋ฆฌ๋Š” ์ œ๋ชฉ, ์ด๋ฏธ์ง€, ๋“œ๋ผ๋งˆ ํšŸ์ˆ˜, ๋ฐฉ์†ก ์‹œ์ž‘ ๋‚ ์งœ๊ฐ€ ์žˆ๋Š” ์นด๋“œ๋ฅผ ๋ณด์—ฌ ์ค€๋‹ค.
    ํ„ฐ๋ฏธ๋„์„ ์—ด๊ณ  python run.py์„ ์‹คํ–‰ํ•˜์‹ญ์‹œ์˜ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์€ ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    ๋ชจ๋“  ๊ฒƒ์„ ๊ธฐ๋กํ•˜๋‹ค


    ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ๊ธฐ๋Šฅ์ด ์™„๋น„๋œflask ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ dockerize๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.์‘์šฉ ํ”„๋กœ๊ทธ๋žจ(flask starter ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ)์˜ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— Dockerfile์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    ๋‹ค์Œ ๊ตฌ์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    #Dockerfile 
    
    FROM python:3.9.7
    
    MAINTAINER Ken Mwaura "[email protected]"
    
    COPY ./requirements.txt /app/requirements.txt
    
    RUN pip install -r requirements.txt
    
    COPY . /app
    
    WORKDIR app
    
    ENV FLASK_APP=run.py
    
    ENV FLASK_ENV=development
    
    EXPOSE 5001:5000
    
    CMD ["flask", "run", "--host", "0.0.0.0"]
    
    ์ฒซ ๋ฒˆ์งธ ์ค„์—์„œ ๊ตฌ์ถ•ํ•  ๊ธฐ๋ณธ ์ด๋ฏธ์ง€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์‹œ์—์„œpython 3.9.7 ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ์„ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค
    ํ™˜๊ฒฝDocker์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์„ค๋ช…์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
  • MAINTAINER ์„ค์ • ์ด๋ฏธ์ง€์˜ ์ž‘์„ฑ์ž ํ•„๋“œ(Docker Hub๋กœ ๋ฐ€์–ด๋„ฃ์„ ๋•Œ ์œ ์šฉ)
  • COPY๊ฐ€ ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜(์†Œ์Šค)์—์„œ ํŒŒ์ผ ๋ณต์‚ฌ๋Œ€์ƒ ๋งค๊ฐœ๋ณ€์ˆ˜(์ด ์˜ˆ์—์„œ๋Š”/app)์ž…๋‹ˆ๋‹ค.
  • RUN์€ ํ•„์š”ํ•œ ํŒŒ์ผ์—์„œ ํ•„์š”ํ•œ ์ข…์† ํ•ญ๋ชฉ์„ ์„ค์น˜ํ•˜๋Š” ๋ฐ pip๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • WORKDIR ์„ค์ • ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ(์ดํ•˜ ๋ชจ๋“  ๋ช…๋ น์€ ์ด ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์‹คํ–‰๋จ).WORKDIR๋Š” ์–ธ์ œ๋“ ์ง€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ENV๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ FLASK_APP์„ ์‹คํ–‰์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.py ํŒŒ์ผ.์‹คํ–‰ํ•  ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.
  • ENV๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ FLASK_ENV์„ development๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.์ด๊ฒƒ์€ flask๊ฐ€ ๊ฐœ๋ฐœ ๋ชจ๋“œ๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์•Œ๋ ค ์ค๋‹ˆ๋‹ค.
  • ์€ docker๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰ํ•˜๋Š” ํฌํŠธ 5001์„ ํฌํŠธ 5000์— ๋น„์ถ”๋Š” ๊ฒƒ์„ ์•Œ๋ ค ์ค๋‹ˆ๋‹ค.
  • CMD๋Š” docker๊ฐ€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋ฌด์—‡์„ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.์šฐ๋ฆฌ์˜ ์˜ˆ์—์„œ flask run ๋ช…๋ น๊ณผ ์ง€์ •ํ•œ ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.
  • ์ด๋ฏธ์ง€๋ฅผ ํ™•๋ฆฝํ•˜๋‹ค


    Dockerfile์„ ์ œ๊ณตํ–ˆ์œผ๋‹ˆ, ๊ตฌ์ถ•์ด ์ •ํ™•ํ•œ์ง€ ํ™•์ธํ•ด ๋ด…์‹œ๋‹ค
    docker build -t flask-starter-app .    
    

    ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰


    ๊ตฌ์ถ•์ด ์™„๋ฃŒ๋˜๋ฉด ์šฉ๊ธฐ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
     docker run --name=flask-app -p 5001:5000 -t -i flask-starter-app  
    
    localhost:5001์œผ๋กœ ์ด๋™ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

    ์ง„์ผ๋ณดํ•œ ์ž๋ฃŒ


    ์˜ฌ๋ฐ”๋ฅธ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.Flask๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํฌํŠธ 5000์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. (Django์™€ ๊ฐ™์€ 8000์ด๋‚˜ Apache์™€ ๊ฐ™์€ 80์ด ์•„๋‹™๋‹ˆ๋‹ค.)
    ์ž์„ธํ•œ ๋‚ด์šฉ์€ Binding Docker Ports์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
    ๋‚˜๋Š” ๋„ค๊ฐ€ ์ด ๋ฌธ์žฅ์„ ์ข‹์•„ํ•˜๊ณ  ๊ณ„๋ฐœ์„ ๋ฐ›์•„ ๋”์šฑ ํ™•์žฅํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž€๋‹ค.๊ณ„์† ์ธ์ฝ”๋”ฉ!๋ฐ‘์ด๋‚˜ ๋ฐ‘์— ๋Œ“๊ธ€ ๋‚จ๊ฒจ์ฃผ์‹œ๋Š” ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.
    ํŠธ์œ„ํ„ฐ์— ์—ฐ๋ฝ:.

    ์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ