[웹개발 종합반] 4주 1일차 개발일지(4주 1-5강)


💻TIL Web.401

0. 들어가며

web401 server

이때까지는 HTML과 CSS, 자바스크립트를 이용해 서버에 갖다줄 파일을 미리 만들어두었다. 이번 주차에서는 요청을 받고, 파일을 갖다주거나 DB에 작업을 하는 데 필요한 서버를 만들 것이다. 서버(server)란 포토샵, 파워포인트, 엑셀처럼 컴퓨터에서 돌아가고 있는 하나의 프로그램이다. 지금은 컴퓨터가 한 대이기 때문에, 한 컴퓨터에서 서버도 만들고 요청도 한다(크롬 브라우저로 접속). 이렇듯 한 컴퓨터에서 클라이언트와 서버를 모두 담당하는 것을 로컬 개발환경이라고 한다. 4주차에는 로컬 개발 환경에서 개발을 하고, 5주차 후반에는 특정 컴퓨터를 사서 서버의 결과물을 올리는 작업을 한다. 그러면 내 컴퓨터를 켜놓지 않아도 우리가 산 컴퓨터를 켜놓음으로써 모든 사람이 접속할 수 있게 된다.


1. 프레임워크

새 폴더를 만들고 파일을 생성한다(venv도 추가). 통상적으로 flask 서버를 돌아가게 만드는 파일 이름은 ‘app.py’로 설정한다. 다음으로, 프레임워크라고 하는 flask 패키지를 서버에 설치한다. 웹 스크래핑을 할 때 requests, beautifulsoup,pymongo 패키지를 가져다 쓴 것처럼, 서버를 만들 때도 다른 개발자분들이 만들어 놓은 패키지(프레임워크)를 가져다 쓴다. 프레임워크(framework)란 남이 짜둔 규칙이나 틀 안에서 내가 코딩을 자유롭게 하는 것이라면, 라이브러리(library)란 내 마음대로 코딩을 하면서 남이 만들어 놓은 것을 중간중간 자유롭게 가져다 쓰는 것이다. 보통 프레임워크는 하나를 정해서 사용하고, 그 안에서 라이브러리는 갯수 제한 없이 가져다 쓸 수 있다.


2. Flask 시작하기

1) 서버 만들기

# app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
  return 'This is Home!'

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

위 코드를 실행시키고 ‘localhost:5000’ 브라우저에 접속하면 화면에 ‘This is Home!’이라는 메세지가 뜬다. 이는 내 컴퓨터에서 서버를 돌리고 있는 것이며, 내 컴퓨터에서 다른 브라우저를 열어서 그곳에 접속한 것이다. ‘localhost’ 뒤의 ‘5000’은 포트(port) 개념으로, 서버의 창구 번호라고 생각하면 된다. 위 코드에서의 port=5000이 창구 번호를 정하는 부분이라고 생각하면 된다. 번호를 바꿀 수도 있지만, 이미 컴퓨터에서 돌아가고 있는 다른 번호와 충돌할 수 있으니 웬만하면 그대로 사용하는 것이 낫다. 서버를 종료하려면 터미널 창에서 Ctrl+C를 누르면 된다.


2) URL 나누기

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
  return 'My first Server!'

@app.route('/mypage')
def mypage():
  return 'My Page!'

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

위처럼 return의 문자열을 바꿀 수 있고, @app.route 부분을 복사해 url을 나눠 새로운 페이지를 만들 수 있다. @app.route의 parameter, 그리고 함수의 이름(homemypage)은 서로 달라야 한다. 브라우저에서 ‘localhost:5000/mypage’에 접속하면 ‘My Page!’가 뜬다. 참고로 ‘localhost:5000’은 ‘/’가 생략되어 있어 ‘localhost:5000/’와 동일하다.


3) HTML 파일 연결하기

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
  return '<button>버튼입니다.</button>'

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

위와 같이 return 부분에 HTML 태그를 사용하면 화면에 그대로 나온다. HTML 태그를 모두 return에 쓸 수도 있겠지만, 너무 길기 때문에 flask 프레임워크를 사용한다.


① Flask의 폴더 구조

프레임워크를 사용할 때는 정해진 규칙을 따라야 하는데, flask는 대표적으로 폴더 구조가 정해져 있다. Flask 서버는 기본적으로 프로젝트 폴더 안에 static 폴더(이미지, CSS 파일), templates 폴더(HTML 파일), app.py 파일을 만들어두고 시작한다. templates 폴더에 index.html 파일을 생성하면, HTML 태그가 자동 생성된다.


② render_template 함수로 HTML 불러오기

# app.py
from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def home():
  return render_template('index.html')

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

위처럼 app.py 파일 최상단에 render_template이라는 flask 내장 함수를 입력하고, return에 HTML 파일 이름을 써주면, flask가 자동으로 templates 폴더 내의 index.html 파일을 클라이언트에 전달한다.

코드의 진행을 살펴보자. 클라이언트가 ‘localhost:5000/’ 엔터를 누르면, @app.route에 따라 home() 함수가 실행된다. 이 함수는 render_template('index.html')을 return하는데, 이때 render_template 함수에 따라 index.html 파일 코드가 그대로 return 뒤에 들어온다. 브라우저는 받은 HTML 파일을 그대로 화면에 그려준다.

여기서 index.html 파일을 바로 브라우저(Chrome)에서 실행시키는 것과 app.py 파일을 통해 화면을 그리는 것의 차이는 무엇일까? app.py 파일을 만들어 실행시키는 것은, 내 컴퓨터에서 만든 서버이긴 하지만, 내가 서버에 요청을 해서 서버에서 가지고 있던 index.html 파일을 받아와서 그린 것이다. 반면 index.html 코드에서 바로 Chrome 버튼을 눌러 실행시키는 것은 단순히 내 컴퓨터에 있는 index.html 파일을 연 것이다. 앞으로는 HTML 파일에서 바로 여는 것이 아니라, 서버를 만들었기 때문에 ‘localhost:5000’를 입력해 접속한다.



3. API 만들기

API란, 2주차 때 배웠듯 ‘서버가 클라이언트에게 데이터 요청을 받는 창구’에 해당한다. 클라이언트가 데이터를 요청할 때, API 또한 규칙과 그에 해당하는 주소가 존재한다. 클라이언트는 HTTP라는 통신 규약을 따르므로, HTTP request method(요청 메서드)를 통해, 응답하는 서버 쪽에 어떠한 요청 종류인지 정보를 알려준다.


1) GET 요청

보통 데이터의 수정이나 변경 없이, 영화 목록을 불러오는 등의 데이터 조회(Read) 요청 시에 사용한다. 데이터 전달 시에는 URL 뒤에 물음표를 붙여 key=value 형태로 전달한다(i.e. google.com?q=bear).

2) POST 요청

GET 요청과 반대로, 회원가입과 같이 데이터를 새로 만들거나(Create) 변경(Update) 또는 삭제(Delete) 요청 시 사용한다. 데이터 전달 시에는 바로 보이지 않는 HTML <body>key=value 형태로 전달한다.


API를 만들기 전에 index.html 파일에 아래 코드와 같이 jQuery를 import하자.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <title>Document</title>
</head>
<body>
    <h1>서버를 만들었다!</h1>
</body>
</html>



📒 2주차에서 서버에 무언가를 요청할 때 Ajax로 call을 했었다. API를 만들고, 클라이언트에서 Ajax로 데이터를 call(요청)해보자.

3) GET 요청 API

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
  return render_template('index.html')

@app.route('/test', methods=['GET'])
def test_get():
  title_receive = request.args.get('title_give')
  print(title_receive)
  return jsonify({'result':'success', 'msg': '이 요청은 GET!'})

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

app.py 파일 최상단에 request, jsonify를 import하고, @app.route에 API 코드를 입력한다. API는 창구이므로 클라이언트에서 요청을 해야 한다. 이 요청은 ‘localhost:5000’ 브라우저에서 ‘우클릭 > 검사’ 또는 ‘Ctrl + Shift + I’로 Console 창을 열고 아래 코드를 입력해 실행한다. Console 창은 클라이언트 사이드라고 볼 수 있다.


$.ajax({
    type: "GET",
    url: "/test?title_give=봄날은간다",
    data: {},
    success: function(response){
      console.log(response)
    }
  })
// {msg: '이 요청은 GET!', result: 'success'}

Console 창의 결과는 위의 Ajax 코드에서 console.log(response)로 출력된 것이다. 이 response는 서버에서 내려준 값으로, 서버 쪽 코드(app.py)를 다시 보면 return에 결과와 똑같은 값이 있는 것을 볼 수 있다.


코드의 진행 과정을 살펴보자. 서버에서는 API, 즉 창구를 만들어 둔 상태다. 클라이언트는 typeGET, url/test, ?title_give=봄날은간다와 같이 물음표 뒤에 key=value 형태로 데이터를 가지고 간다. 이렇듯 클라이언트가 데이터를 가지고 창구에 오면, 창구에서는 request.args.get('title_give')로 고객이 가지고 온 데이터를 가져와 이 값을 title_receive에 저장한다. 예시에 따르면, 이 변수는 ‘봄날은간다’로 바뀌게 된다. 다음으로 print(title_receive)에 따라 변수가 터미널 창에 출력된다. 그 후 return에 따라 클라이언트에 딕셔너리 값을 내려주고, 클라이언트는 그 값을 console.log를 통해 Console 창에 출력한다.

GET 요청에서는 클라이언트가 Ajax로 call(데이터 요청)을 하면, API(창구)에서는 Ajax로 가지고 온 데이터를 처리해서 response를 주고, 클라이언트는 이 값을 받아 console.log로 출력할 수 있다.


4) POST 요청 API

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
  return render_template('index.html')

# GET 요청
@app.route('/test', methods=['GET'])
def test_get():
  title_receive = request.args.get('title_give')
  print(title_receive)
  return jsonify({'result':'success', 'msg': '이 요청은 GET!'})

# POST 요청
@app.route('/test', methods=['POST'])
def test_post():
  title_receive = request.form['title_give']
  print(title_receive)
  return jsonify({'result':'success', 'msg': '이 요청은 POST!'})

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

POST 요청도 GET과 생김새가 조금 다를 뿐 원리는 동일하다. request.form['title_give'] 또한 GET 요청에서와 동일하게 title_give의 값을 가져오라는 의미이다. 이렇게 POST 창구를 만들었다.

$.ajax({
    type: "POST",
    url: "/test",
    data: { title_give:'봄날은간다' },
    success: function(response){
      console.log(response)
    }
  })
// {msg: '이 요청은 POST!', result: 'success'}

POST 요청은 GET 요청과 달리 url이 아닌 data 속성에 값을 넘겨준다. 위와 같이 클라이언트가 Ajax로 call을 하면, API에서는 data로 들어온 title_give 값을 가져와 터미널 창에 출력하고, return으로 딕셔너리를 반환한다.

만약 클라이언트가 서버에서의 title_give가 아니라 title_give2 값을 data에 넘겨주면, Console 창에 ‘INTERNAL SERVER ERROR’라는 에러가 뜬다. 따라서 서버와 클라이언트 Ajax 간의 데이터 변수 이름은 같아야 한다. 참고로 이 에러는 서버 내부에서 에러가 났다는 뜻이므로 app.py 파일에서의 터미널 창 에러를 확인해야 한다.


Categories:

Updated:

Leave a comment