본문 바로가기

반복되는 건 변수로

1. 이번 절의 목표

주소에 변수를 넣고, 데이터를 리스트에 모읍니다. 함수 세 개가 하나로 줄고, 게시물이 100개여도 함수는 하나입니다.

지난 절 끝에서 게시물 함수를 세 개 썼습니다. 같은 모양이 세 번 반복됐습니다. 프로그래밍에서 같은 것이 반복되면 답은 거의 정해져 있습니다. 반복되는 자리를 변수로 뺍니다.

지난 절이번 절
/notice/1 → notice_1()/notice/{id} → notice_detail(id)
/notice/2 → notice_2()(같은 함수)
/notice/3 → notice_3()(같은 함수)

2. 주소에 변수 넣기

main.py에서 게시물 함수 세 개를 지우고 아래 하나로 바꿉니다.

@app.get("/notice/{id}", response_class=HTMLResponse)
def notice_detail(id: int):
    return f"<h1>{id}번 게시물 입니다</h1>"

/notice/1, /notice/2, /notice/3을 다시 쳐 봅니다. 결과는 지난 절과 같습니다. 이번에는 /notice/100도 쳐 봅니다. 함수를 추가하지 않았는데 100번 게시물이 나옵니다.

무슨 일이 일어났는지 세 곳을 봅니다.

  • 주소의 {id}: 중괄호로 감싼 자리는 "여기에 뭐가 오든 받아서 id라는 이름에 담아라"는 뜻입니다.
  • 함수의 id: int: 주소에서 받은 id를 함수가 넘겨받습니다. : int는 "정수로 바꿔서 달라"는 뜻입니다.
  • f"...{id}...": 글자 안에 id 값을 끼워 넣습니다.

파이썬 다시 보기: f-string

f"안녕 {name}"처럼 따옴표 앞에 f를 붙이면, 중괄호 안의 변수가 값으로 바뀝니다. name이 "철수"면 "안녕 철수"가 됩니다. 이 책에서는 화면에 나갈 글자를 만들 때 계속 씁니다.

3. int의 힘

/notice/abc를 쳐 봅니다. 화면에 에러가 나옵니다.

{"detail":[{"type":"int_parsing", ... "Input should be a valid integer" ...}]}

id: int라고 써 두었기 때문에, 숫자가 아닌 것이 오면 FastAPI가 함수를 실행하기도 전에 막습니다. 우리가 검사 코드를 한 줄도 안 썼는데 검사가 됩니다. : int 두 글자가 한 일입니다. 이런 것이 도구를 쓰는 이유입니다.

4. 데이터를 리스트에 담기

지금은 게시물이 번호만 있고 내용이 없습니다. 내용을 넣으려면 어딘가에 담아야 합니다. 이 책에서는 파이썬 리스트에 담습니다.

main.py의 app = FastAPI() 아래에 데이터를 추가하고, 게시판과 게시물 함수를 아래처럼 바꿉니다.

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

data = [
    {'title': 'fastapi 너무 재미있어요! 1', 'contents': '첫 번째 글입니다.'},
    {'title': 'fastapi 너무 재미있어요! 2', 'contents': '두 번째 글입니다.'},
    {'title': 'fastapi 너무 재미있어요! 3', 'contents': '세 번째 글입니다.'},
]


@app.get("/", response_class=HTMLResponse)
def index():
    return "<h1>hello world</h1>"


@app.get("/about", response_class=HTMLResponse)
def about():
    return "<h1>about 입니다</h1>"


@app.get("/notice", response_class=HTMLResponse)
def notice():
    result = ''
    for i in data:
        result += f'<p>{i["title"]}</p>'
    return f"<h1>게시판 입니다</h1>{result}"


@app.get("/notice/{id}", response_class=HTMLResponse)
def notice_detail(id: int):
    if id < 1 or id > len(data):
        return "존재하지 않는 게시물입니다."
    post = data[id - 1]
    return f"""
        <h1>게시물</h1>
        <p>{post['title']}</p>
        <p>{post['contents']}</p>
    """

/notice를 치면 제목 세 개가 나오고, /notice/2를 치면 두 번째 글의 제목과 내용이 나옵니다.

파이썬 다시 보기: 리스트와 딕셔너리

data는 리스트입니다. 대괄호 [ ] 안에 항목이 순서대로 들어 있고, data[0]이 첫 번째입니다. 순서는 0부터 셉니다.

각 항목은 딕셔너리입니다. 중괄호 { } 안에 '이름': 값 짝이 들어 있고, post['title']처럼 이름으로 꺼냅니다.

게시물 하나가 딕셔너리, 게시물 여러 개를 모은 것이 리스트입니다. 백엔드 데이터는 대부분 이 모양입니다.

data의 모양과 주소가 어떻게 연결되는지 그리면 아래와 같습니다.

주소의 번호는 1부터, 리스트의 칸은 0부터입니다. 그래서 코드에 id - 1이 있습니다.

5. 코드 읽기

새로 들어온 줄 몇 개를 읽습니다.

  • for i in data:: 리스트를 처음부터 끝까지 한 항목씩 i에 담아 돕니다. 세 번 돕니다.
  • result += ...: 돌 때마다 <p>제목</p>을 result 뒤에 이어 붙입니다. 다 돌면 <p> 세 개가 붙은 글자가 됩니다.
  • data[id - 1]: 1번 게시물은 data[0]입니다. 사람은 1부터 세고 리스트는 0부터 세기 때문에 하나를 뺍니다.
  • if id < 1 or id > len(data):: 0번이나 4번을 달라고 하면 리스트에 없습니다. 그냥 두면 서버가 에러를 내므로 먼저 막습니다. len(data)는 리스트의 길이, 지금은 3입니다.
  • 세 따옴표 """: 여러 줄짜리 글자를 만들 때 씁니다. HTML이 길어지면 이렇게 씁니다.

6. 여기까지의 그림

주소를 받고, 리스트에서 꺼내고, 글자로 만들어 돌려줍니다. 이것이 백엔드가 하는 일의 전부입니다. 나중에 리스트가 데이터베이스로 바뀌고, 글자가 예쁜 화면으로 바뀌어도 이 세 단계는 그대로입니다.

AI에게 물어보기

아래 코드에서 data[id - 1]에 왜 -1이 붙는지, 그리고 if id < 1 or id > len(data) 검사가 없으면 무슨 일이 생기는지 설명해줘.
(main.py 붙여넣기)
아래 코드의 data 리스트에 게시물을 두 개 더 추가해줘. 제목과 내용은 아무거나 재미있게.
(main.py 붙여넣기)

두 번째 프롬프트로 받은 코드를 붙여 넣으면 /notice에 제목이 다섯 개 나오고 /notice/5도 열립니다. 함수는 하나도 안 바꿨습니다. 데이터와 함수를 나눠 놓으면 이렇게 됩니다.