📝 REST API 란 ?

REST API : REST 기반으로 서비스 API(Application Interface) 를 구현한 것을 REST API 라고 한다.

 

[Flask] (2) REST & HTTP Method & API/End Point

📝 REST란 무엇인가 ? REST : Representational State Transfer = 자원을 이름(자원의 표현)으로 구분하여 해당 자원의 상태를 주고 받는 것을 의미한다. = 즉, 자원(resourece) 의 표현(representation)에 의한 "..

youngminieo1005.tistory.com

최근 OpenAPI (누구나 사용할 수 있도록 공개된 API), 마이크로 서비스 등을 제공하는 기업에서는 대부분 REST API를 제공한다.

💡 REST API 특징

  • REST 기반으로 시스템을 분산하여 확장성과 재사용성을 높여 유지보수를 편리하게 할 수  있다.
  • REST는 HTTP 표준을 기반으로 구현하고, HTTP를 지원하는 프로그램 언어로 클라이언트,서버를 구현할 수 있다.

따라서, REST API를 구현하면, 클라이언트 뿐만 아니라, JAVA, C#, WEB 등을 이용해서 클라이언트를 제작할 수 있다.

💡 REST API 규칙

REST에서 가장 중요한 기본적인 규칙은 두 가지이다.
URI는 자원을 표현하는 데 집중학, 행위에 대한 정의는 HTTP Method를 통해 하는 것이 REST API를 설계하는 핵심이다.

1. URI는 정보의 자원을 표현해야 한다.

  • 리소스명은 동사보다는 명사를 사용한다.
  • URI는 자원을 표현하는 데에 중점을 두어야한다.
  • GET같은 행위에 대한 표현이 들어가서는 안된다.
#안 좋은 예시
GET /getTodos/3
GET /todos/show/3

#좋은 예시
GET /todos/3

2. 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE 등)으로 표현한다.

주로 5가지의 Method(GET, POST, PUT, DELETE , PATCH)를 사용해서 CRUD를 구현한다.

[ 출처 : elice ]

#안 좋은 예시
GET /todos/delete/3

#좋은 예시
DELETE /todos/3

 

📝 RESTful 이란 ?

  • RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어이다.
  • 따라서 REST API를 사용하는 웹 서비스를 우리는 "RESTful하다"고 할 수 있다.
  • RESTful은 REST를 REST답게 쓰기 위한 방법으로 누군가가 공식적으로 발표한 것이 아니다.
  • REST의 원리를 따르고 사용하는 시스템을 RESTful이라는 용어로 칭하게 된다.

💡 RESTful의 목적은 무엇인가 ?

이해하기 쉽고 쉬운 REST API를 만드는 것이다.

  • RESTful한 API를 구현하는 근본적인 목적이 성능 향상이 중점이 아니다.
  • API의 이해도와 호환성을 높이는 것이 주된 목적이다.
  • 따라서, 성능이 중요한 상황에서는 굳이 RESTful한 API를 구현할 필요가 없다.

 

📝 REST API 설계 기본 규칙

1. URI는 정보의 자원을 표현해야 한다.

  • 자원은 동사보다는 명사를 사용
  • 자원은 대문자보다는 소문자 사용
  • 자원의 도큐먼트 이름으로는 단수 명사를 사용
  • 자원의 컬렉션 이름으로는 복수 명사를 사용
  • 자원의 스토어 이름으로는 복수 명사를 사용
#안 좋은 예시
GET /Student/3

#좋은 예시
GET /students/3		# 소문자,복수명사 사용

2. 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE 등)로 표현한다.

- URI에 HTTP Method가 들어가면 안된다.

#안 좋은 예시
GET /students/delete/3

#좋은 예시
DELETE /students/3

- CRUD 기능을 나타내는 것은 URI에 사용하지 않는다.

#안 좋은 예시
GET /students/show/3
GET /students/insert/4

#좋은 예시
GET /students/3
POST /students/4
  • : id 는 하나의 특정 자원을 나타내는 고유값이다.

    ex) student를 생성하는 route : POST/students
    ex) id=12인 student를 삭제하는 route: DELETE/students/12

🔊 용어

  • 도규먼트 : 객체 인스턴스나 데이터베이스 레코드와 유사한 개념
  • 컬렉션 : 서버에서 관리하는 디렉터리라는 자원
  • 스토어 : 클라이언트에서 관리하는 자원 저장소

 

📝 REST API 설계 규칙

1. 슬래시 (/) 는 계층 관계를 나타내는 데 사용한다.

ex) https://academy.elice.io/classroom/teach 

2. URI 마지막 문자로 슬래시 (/) 를 포함하지 않는다.

ex) https://academy.elice.io/classroom/teach/ ← ( X )

  • URI에 포함되는 모든 글자는 자원의 유일한 식별자로 사용되야 한다.
  • URI가 다르다는 것은 불러오는 자원이 다르다는 뜻이고, 반대로 자원이 다르면 URI도 달라져야 한다.
  • REST API는 분명한 URI를 만들어 통신을 해야 하기 때문에 혼동을 주지 않도록 URI 경로의 마지막에는
    슬래시 (/) 를 사용하지 않는다.

3. 하이픈 (-) 은 URI 가독성을 높이는데 사용한다.

4. 밑줄 ( _ ) 은 URI에 사용하지 않는다.

5. URI 경로에는 소문자를 사용한다.

6. 파일확장자는 URI에 포함하지 않는다.

ex) https://academy.elice.io/classroom/teach/111/python.png ← ( X )
ex) GET / classroom/teach/111/python HTTP/1.1 Host: academy.elice.io Accept: image/png ← ( O )

7. 자원 간에 연관 관계가 있는 경우 다음과 같이 작성한다.

/자원명/자원ID/관계가 있는 다른 자원명
ex) GET : /students/{studentid}/classroom

[ 출처 : elice ]

 

📝 REST API : CRUD 구현해보기

앞선 포스팅 ( Flask 1~ 6 )을 보면, render_template( ) 메소드를 이용해서, html에 출력 결과를 띄워 값들을 확인했었다.그리고 html에서는 jinja2 문법으로 해당 내용을 받아서 html에 띄울 수 있었다.

하지만, 이는 "API 서버"를 개발한 것이 아닌, Flask를 활용해서 "웹 어플리케이션"을 개발한 것이다.

REST API 서버를 개발 할 때는, 서버단에서 직접 렌더링(Rendering)해서 페이지를 보여주는 것이 아니라
웹 페이지를 구성하는데 필요한 "정보를 반환" 해주도록 구현해야 한다.

따라서, 백엔드 서버는 클라이언트(프론트 엔드) 와 데이터를 주고 받음으로써 통신을 하게 되는 것이다.

🔊 웹 상에서 가장 많이 쓰이는 데이터 형식은 JSON 형식의 데이터를 주고 받는다.

🔊 [Tips] REST API를 위해 구현되는 CRUD의 메소드와 URL의 이름은 CRUD에 맞추는 것이 일반적이다.

 

from flask import Flask, render_template, request, redirect, url_for
import json

app = Flask(__name__)

board = []  # 실제 DB가 아닌, 예제를 위한 board 리스트

# root 경로 
@app.route('/')
def index():
    return render_template("Boards.html", rows = board)

# POST : CREATE (C)
@app.route("/create", methods=["POST"])
def create():
    name = request.form["name"]
    context = request.form["context"]
    board.append([name,context])

    return json.dumps( { "status":200, "result" : { "id" : len(board)} } )  # json.dumps({ HTTP 상태, 반환할 결과 }) = 데이터를 json 형태로 변환해준다.


# GET : READ (R)
@app.route("/read", methods=["GET"])
def read():
    return json.dumps({"status":200, "result":board})


if __name__ == '__main__':
    app.run()
<!-- ./templates/Boards.html -->

<!doctype html>
<html lang="ko">
    <head>
        <meta charset="UTF-8">
        <meta name="Generator" content="EditPlus®">
        <meta name="Author" content="">
        <meta name="Keywords" content="">
        <meta name="Description" content="">
        <title>게시판 등록</title>
        <style type="text/css">
            body{ text-align: center; }
        </style>
     </head>
    <body>
        <h1>게시판</h1>
        <h4>추가</h4>
        <form action = "/create" method = "POST">
            이름<br>
            <input type = "text" name = "name" /><br>
            내용<br>    
            <textarea name = "context" cols="50" rows="10"></textarea><br><br>
            <input type = "submit" value = "게 시" /><br>
        </form>
        <h4>목록보기</h4>
        <table class="table" border="1" width = 600 style = "word-break:break-all" style="table-layout: fixed" align="center">
        <thread>
            <th width="4%">목차</th>
            <th width="15%">이름</th>
            <th width="25%">내용</th>
        </thread>
        {% for row in rows %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
        </tr>
        {% endfor %}
        </table>
    </body>
</html>

💡 CREATE : POST

페이지에서 데이터 입력
입력된 값을 json 형태로 변환한것을 반환한 결과

 

💡 READ : GET

/read 로 GET 했을 때 ( 데이터 하나도 입력 안한상황 )

 

📌 UPDATE(PUT)와 DELETE(DLEDTE)는 앞선, CREATE(POST), READ(GET)과 달리, Ajax를 이용해서 구현되기도 한다.

📝 Ajax : 비동기식(Asynchronous)  JavaScript + XML 이다.

Ajax 는 REST API를 손쉽게 구현하기 위해 사용되는 "프레임워크"이며, html 파일에서 간단히 사용하는 방법을 해보자.
( 이번에는, html 파일을 더, 주의깊게 보자. ( Ajax 부분 ) )

# Ajax_Example.py

from flask import Flask, render_template, request, jsonify  # Flask의 내장된 jsonify = json.dump() 와 같은 기능이다. json 형태로 데이터 반환

app = Flask(__name__)

board = []  # 임시 DB 개념 (사실,리스트)

# root
@app.route("/")
def index():
    return render_template("ajax_index.html", rows=board)

@app.route("/ajax", methods=["POST"])
def ajax():
    data = request.get_json()   # request.get_json() = POST 요청을 통해 얻은 데이터 -> json 형식으로 얻기 위한 메소드
    board.append(data)

    return jsonify(result="success", result2=data)  # json_dumps({}) 와 기능은 같다.

if __name__ == '__main__':
    app.run()
<!--ajax_index.html-->

<html>

<head>
    <script src="https://code.jquery.com/jquery-latest.min.js"></script>
</head>

<body>
    <p id="example">AJAX</p>
    <input type="text" id="id1" placeholder="id">
    <input type="text" id="name1" placeholder="name">
    <input type="text" id="context1" placeholder="context">
    <input type="button" id="execute" value="execute">


    <script>
        $('#execute').click(function () {
            var id = $('#id1').val();
            var name = $('#name1').val();
            var context = $('#context1').val();

            var postdata = {
                'id': id, 'name': name, 'context': context
            }
            
            // ajax 형식으로 -> ajax example.py로 데이터 전송
            $.ajax({
                type: 'POST',
                url: '{{url_for("ajax")}}',
                data: JSON.stringify(postdata),		// 서버로 데이터를 넘길때는, JSON.stringify(데이터)로 String형식으로 넘겨준다.
                dataType: 'JSON',
                contentType: "application/json",
                success: function (data) {
                    alert('성공! 데이터 값:' + data.result2['id'] + " " + data.result2['name'] + " " + data.result2['context'])
                },
                error: function (request, status, error) {
                    alert('ajax 통신 실패')
                    alert(error);
                }
            })
        })
    </script>
    <table border=1 width="600">
        <thead>
            <td>목차</td>
            <td>이름</td>
            <td>내용</td>
        </thead>

        {% for row in rows %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ row['name'] }}</td>
            <td>{{ row['context'] }}</td>
        </tr>
        {% endfor %}
    </table>

</body>

</html>

 

 

💡 UPDATE & DELETE ( PUT & DELETE )

from flask import Flask, render_template, jsonify, request

app = Flask(__name__)

board = [{"id": 1, "name": "elice", "context": "test"}]

@app.route('/')
def index():
    return render_template('index.html', rows = board)

@app.route('/create', methods=['POST'])
def create():
    data = request.get_json()
    board.append(data)
    return jsonify(result = "success", result2= data)


@app.route('/delete', methods=['POST'])
def delete():
    del board[-1]

    return jsonify(result="success")
    

@app.route('/put', methods=['POST'])
def put():
    
    data = request.get_json()
    board.append(data)

    return jsonify(result="success",result2=data)
<!--index.html-->

<html>
<head>
    <script src="https://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
    <p id="example">AJAX</p>
    <input type="text" id="id1" placeholder="id">
    <input type="text" id="name1" placeholder="name">
    <input type="text" id="context1" placeholder="context">
    <input type="button" id="create" value="create">
    <input type="button" id="update" value="update">
    <input type="button" id="delete" value="delete">
    
    
    <script>
    	// POST (CREATE)
        $('#create').click(function(){
            var id = $('#id1').val();
            var name = $('#name1').val();
            var context = $('#context1').val();

            var postdata = {
                'id':id, 'name':name, 'context':context
            }
            $.ajax({
                type: 'POST',
                url: '{{url_for("create")}}',
                data: JSON.stringify(postdata),
                dataType : 'JSON',
                contentType: "application/json",
                success: function(data){
                    alert('성공! 데이터 값:' + data.result2['id']+" " + data.result2['name']+ " " + data.result2['context'])
                },
                error: function(request, status, error){
                    alert('ajax 통신 실패')
                    alert(error);
                }
            })
        })
        
        // PUT (UPDATE)
        $('#update').click(function(){
            var id = $('#id1').val();
            var name = $('#name1').val();
            var context = $('#context1').val();

            var postdata = {
                'id':id, 'name':name, 'context':context
            }
            $.ajax({
                type: 'POST',
                url: '{{url_for("put")}}',
                data: JSON.stringify(postdata),
                dataType : 'JSON',
                contentType: "application/json",
                success: function(data){
                    alert('성공! 수정된 데이터 값:' + data.result2['id']+" " + data.result2['name']+ " " + data.result2['context'])
                },
                error: function(request, status, error){
                    alert('ajax 통신 실패')
                    alert(error);
                }
            })
        })
        
        // DELETE (DELETE)
        $('#delete').click(function(){
            $.ajax({
                type: 'POST',
                url: '{{url_for("delete")}}',
                contentType: "application/json",
                success: function(){
                    alert('성공! 데이터 삭제 완료')
                },
                error: function(request, status, error){
                    alert('ajax 통신 실패')
                    alert(error);
                }
            })
        })
    </script>
	<table border=1 width="600">
        <thead>
        <td>목차</td>
		<td>이름</td>
		<td>내용</td>
		</thead>
        
    {% for row in rows %}
		<tr>
            <td>{{ loop.index }}</td>
			<td>{{ row['name'] }}</td>
			<td>{{ row['context'] }}</td>
		</tr>
	{% endfor %}
	</table>
</body>
</html>

 

728x90
반응형

📝 ORM

ORM이란, 객체 관계 매핑(Object Relational Mapping)으로, 데이터베이스 내의 테이블들을 객체화해서, 각 DBMS에 대해서 CRUD 등을 공통된 접근 기법으로 사용할 수 있다.

일반적으로 하나의 객체정보를 다루기 위해서 여러 SQL 쿼리를 사용하게 되고, 프로그램이 커질수록 작성해야 하는 SQL 쿼리가 많아져 복잡해진다.

반복되는 쿼리를 객체 단위로 생성하여, 이를 해결하고자 했고, 이런 작업을 도와주는 것을 바로 ORM이라고 한다. 

ORM을 사용하게 되면 따로 SQL문을 작성할 필요없이 객체를 통해 간접적으로 데이터베이스를 조작할 수 있다.

🔊 대표적인 python ORM = DjangoORM 과 SQLAlchemy 등이 있다.

💡 장점

  • 개발 생산성을 증가시킨다. 프로그래머는 DBMS에 대한 큰 고민없이, ORM에 대한 이해만으로
    대부분의 CRUD를 다룰 수 있다.
  • 유지보수성이 좋다. 데이터 구조 변경시, 객체에 대한 변경만 이루어지면 된다.
  • 코드 가독성이 좋다. 객체를 통해서 대부분의 데이터를 접근 및 수정을 진행한다.

💡 단점

  • 호출 방식에 따라 성능이 천차만별이다.
  • 복잡한 쿼리 작성시, ORM 사용에 대한 난이도가 증가한다.
  • DBMS(MySQL) 고유의 기능을 모두 사용하지 못한다.

 

📌 SQL 쿼리와 ORM의 차이점

다음과 같은 DB 테이블이 있고, 데이터를 삽입하려고 한다고 가정하자.

[ 출처 : elice ]

INSERT INTO 엘리스 (name, age) VALUES (‘여왕’, ‘18’);
member = Member(name=‘여왕’, age=‘18’)
db.session.add(member)

위에가 SQL, 아래가 ORM 방식이다.

 

📝 SQLAlchemy

파이썬 ORM 라이브러리에서 가장 많이 사용되는 SQLAlchemy파이썬 코드에서 Flask를 DB와 연결하기 위해 사용된다. SQLAlchemy는 ORM이고, DB 테이블을 프로그래밍 언어의 "클래스"로 표현하게 해주고, 테이블의 CRUD 등을 돕는다.

💡 SQLAlchemy 는 왜 사용하나 ?

  • SQLAlchemy를 사용하면, SQL쿼리를 사용하지 않고, 프로그램이 언어로 객체간의 관계를 표현할 수 있다.
  • SQLAlchemy로 Model을 정의하고, 정의한 모델을 테이블과 Mapping 할 수 있는 여러가지 방법을 쉽게 구현할 수 있다.
  • 이는, DB의 종류와 상관 없이 일관된 코드를 유지할 수 있어 프로그램 유지 및 보수가 편리하고 SQL 쿼리를 내부에서 자동으로 생성하기 때문에 오류 발생률이 줄어드는 장점이 있다.

 

📌 Model 구현

Member는 파이썬 클래스이다. 클래스는 DB의 테이블과 Mapping하여 사용되며 DB의 테이블과 매핑되는 이 클래스를 모델이라고 한다.

SQLAlchemy에 내장되어 있는 Mode 클래스를 상속해서, 이를 구현할 수  있으며, 쿼리를 사용할 수 있다.

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
name = db.Column(db.String(20), primary_key = True)
age = db.Column(db.Integer, nullable=False)

💡 쿼리의 종류

일반적인 SQL 의 사용법과 비슷하다.
다만, 완전히 SQL이 아니라, 프로그래밍언어 적으로 사용해서, 사용방법이 조금씩은 다른 것 같다.

[ 출처 : elice ]

💡 사용 예시

  • equal ( == )
@app.route('/')
def list():
    member_list = Member.query.filter(Member.name == 'Elice')
    return " ".join(i.name for i in member_list)
  • not equal ( != )
@app.route('/')
def list():
    member_list = Member.query.filter(Member.name != 'Elice')
    return " ".join(i.name for i in member_list)
  • like ( like( ) )
@app.route('/')
def list():
    member_list = Member.query.filter(Member.name.like('Elice'))
    return " ".join(i.name for i in member_list)
  • in
@app.route('/')
def list():
    member_list = Member.query.filter(Member.name.in_(['Elice', 'Dodo']))
    return " ".join(i.name for i in member_list)
  • not in
@app.route('/')
def list():
    member_list = Member.query.filter(~Member.name.in_(['Elice', 'Dodo']))
    return " ".join(i.name for i in member_list)
  • is null
@app.route('/')
def list():
    member_list = Member.query.filter(Member.name == None)
    return " ".join(i.name for i in member_list)
  • is not null
@app.route('/')
def list():
    member_list = Member.query.filter(Member.name != None)
    return " ".join(i.name for i in member_list)
  • and
@app.route('/')
def list():
    member_list = Member.query.filter((Member.name == 'Elice') & (Member.age == '15'))
    return " ".join(i.name for i in member_list)
  • or
@app.route('/')
def list():
    member_list = Member.query.filter ((Member.name == 'Elice') | (Member.age == '15'))
    return " ".join(i.name for i in member_list)
  • order by
@app.route('/')
def list():
    member_list = Member.query.order_by(Member.age.desc())
    return " ".join(i.name for i in member_list)
  • limit
@app.route('/')
def list(limit_num = 5):
    if limit_num is None:
        limit_num = 5
    member_list = Member.query.order_by(Member.age.desc()).limit(limit_num)
    return " ".join(i.name for i in member_list)
  • offset
@app.route('/')
def list(off_set = 5):
    if off_set is None:
        off_set = 5
    member_list = Member.query.order_by(Member.age.desc()).offset(off_set)
    return " ".join(i.name for i in member_list)
  • count
@app.route('/')
def list():
    member_list = Member.query.order_by(Member.age.desc()).count()
    return str(member_list)

 

📝 Model 생성 예제

  • [ 참고 ] 저장된 DB를 확인할 때는 두 가지 방법으로 확인할 수 있다.
  • Member객체.query.all( ) = 리스트형으로 반환
  • Member객체.query.first( ) = 모델 하나의 객체로 반환하기 때문에 유의
# models.py

from main import db		# main.py에서 생성한 SQLAlchemy 객체인 db import

class Member(db.Model):		# db의 Model 클래스 상속, Member 모델 생성
	
    # Member 객체의 Atrribute 설정
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False) 
    age = db.Column(db.Integer, nullable=False)
	
    # Member 객체 생성자
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
# main.py

from flask import Flask, request, render_template, redirect, url_for
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import config
db = SQLAlchemy()
migrate = Migrate()

app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
migrate.init_app(app, db)

from models import Member	# main.py에서 Member 클래스를 import

@app.route('/')
def _list():
    name = ['Elice', 'Dodo', 'Checher', 'Queen']
    age = 15
	
    # 주어진 name 리스트를 사용하고, 나이와 함께, DB에 추가
    for data in name:
        member = Member(data,age)
        age += 1
        db.session.add(member)
        

    db.session.commit()		# DB를 commit()

    member_list = Member.query.all()	# member_list에 Member의 모든 튜플 저장

    if(type(member_list)!=type([])):
        member_list=[member_list]
    return render_template('member_list.html', member_list=member_list)

if __name__ == "__main__":
    app.run()
# config.py

import os
BASE_DIR = os.path.dirname(__file__)

SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'main.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False
<!-- ./templates/member_list.html -->

<!DOCTYPE html>
{% if member_list %}
    <ul>
        {% for member in member_list %}
             <li><p>{{ member.name }} {{ member.age }}</p></li>
        {% endfor %}
    </ul>
{% else %}
    <p>멤버가 없습니다.</p>
{% endif %}

[ 출처 : elice ]

 

📝 Model 사용해서 자료추가 예제

# models.py

from main import db

class Member(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    age = db.Column(db.Integer, nullable=False)
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
# main.py

from flask import Flask, request, render_template, redirect, url_for
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import config

db = SQLAlchemy()
migrate = Migrate()

app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
migrate.init_app(app, db)
from models import Member


@app.route('/list')
def _list():
    member_list = Member.query.all()
    return render_template('member_list.html', member_list=member_list)

@app.route("/", methods=["GET","POST"])
def _add():

    if request.method == 'POST':
        name = request.form['name']
   
        try:
            age = int(request.form["age"])
        except:
            return "나이는 숫자만 입력하세요."
            
        member = Member(name, age)
        db.session.add(member)
        db.session.commit()

        return redirect(url_for("_list"))
        
    else:
        return render_template('add.html')
if __name__ == "__main__":
    app.run()
# config.py

import os
BASE_DIR = os.path.dirname(__file__)

SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'main.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False
<!-- ./templates/add.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
    <form action = "/" method="POST">
        <p>name : <input type="text" id = "name" name = "name"></p>
        <p>age : <input type="text" id = "age" name = "age"></p>
        <p>이름과 나이를 입력하고 제출버튼을 누르세요. <br><input type = "submit" value = "제출"/> </p>
    </form>
</body>
</html>


<!-- ./templates/member_list.html -->
<!DOCTYPE html>
{% if member_list %}
    <ul>
        {% for member in member_list %}
             <li><p>{{ member.name }} {{ member.age }}</p></li>
        {% endfor %}
    </ul>
{% else %}
    <p>멤버가 없습니다.</p>
{% endif %}

[ 출처 : elice ]

 

📝 Query 사용법 예제 ( 몇 가지만 )

  • equal, not equal, like
# main.py 에 추가
# config.py , models.py 는 동일

@app.route('/search', methods=['GET', 'POST'])
def _search():
    if request.method == 'POST':
        key = request.form['keyword']
        con = request.form['condition']
        if(con=='1'):
            member_list = Member.query.filter(Member.age == int(key))
        elif(con=='2'):
            member_list = Member.query.filter(Member.age != int(key))
        elif(con=='3'):
            member_list = Member.query.filter(Member.age.like(int(key)))
            
        return render_template('member_list.html', member_list=member_list)
    else:
        return render_template('search.html')
<!-- ./templates/search.html 추가 -->
<!-- add.html , member_list.html 은 model 생성 예제와 동일 -->

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
    <form action = "/search" method="POST">
        <p><input type="text" id = "keyword" name = "keyword"></p>
        <p><input type="radio" name = "condition" value ="1" checked="checked">equal <input type="radio" name = "condition" value ="2">not equal <input type="radio" name = "condition" value ="3">like</p>
        <input type = "submit" value = "검색"/>
    </form>
</body>
</html>

[ 출처 : elice ]

  • in, not in, is null, is not null, and, or, order by, limit, offset 또한 위에 Member객체.query.filter( ) 안에 작성하는 것과 비슷해서 생략 ( 사용법만 필요할 때 숙지 )

 

728x90
반응형

📝 RDB

데이터베이스(DataBase)의 종류는 크게 1. 관계형 데이터베이스(RDB, 2. NoSQL(Not Only SQL)로 나뉜다.

RDB(Relation Database)는 관계형 데이터 모델을 기반으로 한 데이터 베이스다.
다시 말해, 키(Key) - 값(Value)들의 간단한 관계를 테이블화한 데이터베이스다. RDB는 다음 특징을 가진다.

  • 데이터 독립성이 높다.
  • 고수준의 DML을 사용해서, 결합, 제양, 투영 등의 관계 조작에 의해 비약적으로 표현 능력을 높일 수 있다.
  • 이들의 관계 조작에 의해 자유롭게 구조를 변경할 수 있다.

💡 RDB의 종류

  • Oracle
  • MySQL
  • MS-SQL
  • DB2
  • Maria DB
  • Derby
  • SQLite

 

📝 RDB와 Flask의 상호작용

Flask에서 RDB를 연동하면 어떻게 될까 ? Flask에서 입력 받은 내용들을 DB에 저장할 수 있어야 한다.

= 효율적인 데이터 관리 기능 제공

파이썬은 오픈 소스와 상용 데이터베이스에 대한 대부분의 데이터베이스 엔진을 위한 패키지를 가지고 있다.
앞으로의 포스팅에서는 그 중, sqlite3 와 Flask 어플리케이션 안에 있는 SQLAlchemy를 사용해서 진행한다.

SQLAlchemy파이썬 코드에 DB와 연결하기 위해 사용되는 라이브러리다.

[ 출처 : elice ]

 

📝 Flask - RDB 예제 : 게시판 구현하기

💡 DB 사용자 추가

from flask import Flask, render_template, request, url_for, redirect
import sqlite3 # salite3

app = Flask(__name__)
conn = sqlite3.connect("database.db")   # splite3 db 연결
print("Opened database successfully")
conn.execute("CREATE TABLE IF NOT EXISTS Board(name TEXT, context TEXT)")   # Board 라는 DB생성
print("TABLE Created Successfully")
name = [
    ["Elice", 15],
    ["Dodo", 16],
    ["checher", 17],
    ["Queen", 18]
]
for i in range(len(name)):
    conn.execute(f"INSERT INTO Board(name,context) VALUES('{name[i][0]}', '{name[i][1]}')")  # Board DB에 데이터 삽입
conn.commit()   # 지금껏 작성한 SQL, DB에 반영 commit
conn.close()    # 작성 다한 DB는 닫아줘야함 close

# ================= 여기서부터는 다시 Flask 영역 ==========================

@app.route('/')
def board():
    con = sqlite3.connect("database.db")
    cur = con.cursor()
    cur.execute("SELECT * FROM Board")
    rows = cur.fetchall()

    print("DB: ")
    for i in range(len(rows)):
        print(rows[i][0] + ':' + rows[i][1])
    return render_template("board1.html", rows = rows)


@app.route("/search", methods=["GET","POST"])
def search():
    if request.method == "POST":
        name = request.form["name"] # search.html 가보면, form에 name만 받기로 함.
        con = sqlite3.connect("database.db")
        cur = con.cursor()
        cur.execute(f"SELECT * FROM Board WHERE name='{name}'")
        rows = cur.fetchall()
        print("DB : ")
        for i in range(len(rows)):
            print(rows[i][0] + ':' + rows[i][1])
        return render_template("search.html", rows=rows)
    else:
        return render_template("search.html")

@app.route("/add", methods=["GET","POST"])
def add():
    if request.method == "POST":
        try:
            name = request.form["name"]
            context = request.form["context"]

            # DB에 접근해서, 데이터를 삽입할때는, 직접 DB를 열어야되는데, 윗 과정처럼, close까지 하기 힘드니깐, 하는 방식, 결과는 같은 것 !
            with sqlite3.connect("database.db") as con:
                cur = con.cursor()
                cur.execute(f"INSERT INTO Board(name,context) VALUES('{name}','{context}')")
                con.commit()
        except:
            con.rollback()  # DB 롤백함수, SQL이 오류나면, 반영전, 이전 상태로 돌리는 것
        finally:
            return redirect(url_for("board"))
    else:
        return render_template("add.html")


if __name__ == '__main__':
    app.run()
<!-- ./templates/board1.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4><a href="{{url_for('add')}}">추가</a> <a href="{{url_for('search')}}">검색</a><br><br>목록</h4>
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
        </tr>
        {% endfor %}
    </table>
</body>

</html>
<!-- ./templates/add.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4>추가</h4>
    <form action="/add" method="POST">
        이름<br>
        <input type="text" name="name" /><br>
        내용<br>
        <input type="text" name="context" style="text-align:center; width:400px; height:100px;" /><br><br>
        <input type="submit" value="게 시" /><br>
    </form>
</body>

</html>
<!-- ./templates/search.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <form action="/search" method="POST">
        <input type="text" name="name" />
        <input type="submit" value="검 색" /><br>
    </form>
    <h4>검색결과</h4>
    {% if rows%}
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
        </tr>
        {% endfor %}
        {% else %}
        <p> 검색결과가 없습니다. </p>
        {% endif %}
    </table>
</body>

</html>

[ 출처 : elice ]

 

💡 중복 사용자 제어 ( 위에, DB 사용자 추가에서 변한 코드는 몇 없음

from flask import Flask, render_template, request, url_for, redirect
import sqlite3 # salite3

app = Flask(__name__)
conn = sqlite3.connect("database.db")   # splite3 db 연결
print("Opened database successfully")
conn.execute("CREATE TABLE IF NOT EXISTS Board(name TEXT, context TEXT)")   # Board 라는 DB생성
print("TABLE Created Successfully")
name = [
    ["Elice", 15],
    ["Dodo", 16],
    ["checher", 17],
    ["Queen", 18]
]
for i in range(len(name)):
    conn.execute(f"INSERT INTO Board(name,context) VALUES('{name[i][0]}', '{name[i][1]}')")  # Board DB에 데이터 삽입
conn.commit()   # 지금껏 작성한 SQL, DB에 반영 commit
conn.close()    # 작성 다한 DB는 닫아줘야함 close

# ================= 여기서부터는 다시 Flask 영역 ==========================

@app.route('/')
def board():
    con = sqlite3.connect("database.db")
    cur = con.cursor()
    cur.execute("SELECT * FROM Board")
    rows = cur.fetchall()

    print("DB: ")
    for i in range(len(rows)):
        print(rows[i][0] + ':' + rows[i][1])
    return render_template("board1.html", rows = rows)


@app.route("/search", methods=["GET","POST"])
def search():
    if request.method == "POST":
        name = request.form["name"] # search.html 가보면, form에 name만 받기로 함.
        con = sqlite3.connect("database.db")
        cur = con.cursor()
        cur.execute(f"SELECT * FROM Board WHERE name='{name}'")
        rows = cur.fetchall()
        print("DB : ")
        for i in range(len(rows)):
            print(rows[i][0] + ':' + rows[i][1])
        return render_template("search.html", rows=rows)
    else:
        return render_template("search.html", msg ="검색어를 입력해주세요.")

@app.route("/add", methods=["GET","POST"])
def add():
    if request.method == "POST":

        name = request.form["name"]
        context = request.form["context"]

        # DB에 접근해서, 데이터를 삽입할때는, 직접 DB를 열어야되는데, 윗 과정처럼, close까지 하기 힘드니깐, 하는 방식, 결과는 같은 것 !
        with sqlite3.connect("database.db") as con:
            cur = con.cursor()
            cur.execute(f"SELECT count(*) FROM Board WHERE name='{name}'")
            
            # 회원 한명, 추가할라 했는데, 그전에 들어온, name값이랑 같은 이름이 DB에 있으면, 중복회원자이므로, 못하게 제어함
            if cur.fetchall()[0][0] == 0:   # 중복이름이 없으면
                cur.execute(f"INSERT INTO Board(name,context) VALUES('{name}','{context}')")
                con.commit()
                cur.execute("SELECT * FROM Board")
                rows = cur.fetchall()
                return render_template("board1.html",rows= rows)        
            else:   # 중복이름이 있으면
                return render_template("add.html",msg = "중복사용자가 있습니다.")
    else:
        return render_template("add.html")


if __name__ == '__main__':
    app.run()
<!-- ./templates/board1.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4><a href="{{url_for('add')}}">추가</a> <a href="{{url_for('search')}}">검색</a><br><br>목록</h4>
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
        </tr>
        {% endfor %}
    </table>
</body>

</html>
<!-- ./templates/add.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4>추가</h4>
    <form action="/add" method="POST">
        이름<br>
        <input type="text" name="name" /><br>
        내용<br>
        <input type="text" name="context" style="text-align:center; width:400px; height:100px;" /><br><br>
        <input type="submit" value="게 시" /><br>
    </form>
    {% if msg %}
    <p> {{ msg }} </p>
    {% endif %}
</body>

</html>
<!-- ./templates/reaserch.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <form action="/search" method="POST">
        <input type="text" name="name" />
        <input type="submit" value="검 색" /><br>
    </form>
    <h4>검색결과</h4>
    {% if rows%}
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
        </tr>
        {% endfor %}
        {% elif msg %}
        <p> {{ msg }} </p>
        {% else %}
        <p> 검색결과가 없습니다. </p>
        {% endif %}
    </table>
</body>

</html>

[ 출처 : elice ]

 

💡 게시판 내용 생성 및 조회

위에서, DB 사용자 생성, 중복제거 내용과 동일

 

💡 게시판 내용 수정 및 삭제

#DATABASE
from flask import Flask, render_template, request, url_for, redirect
import sqlite3
app = Flask(__name__)
conn = sqlite3.connect('database.db')
print ("Opened database successfully")
conn.execute("DROP TABLE IF EXISTS Board")  # Board 테이블이 기존에 있다면 삭제 (매번, 동일한 파일에서 실행하면, 내용이 겹쳐서 만듦)
conn.execute('CREATE TABLE IF NOT EXISTS Board (name TEXT, context TEXT)')  # Board 테이블이 기존에 없다면 생성
print ("Table created successfully")
name = [['Elice', 15], ['Dodo', 16], ['Checher', 17], ['Queen', 18]]
for i in range(4):
    conn.execute(f"INSERT INTO Board(name, context) VALUES('{name[i][0]}', '{name[i][1]}')")
conn.commit()
conn.close()

# root = home
@app.route('/')
def board():
    con = sqlite3.connect("database.db")
    cur = con.cursor()
    cur.execute("select * from Board")
    rows = cur.fetchall()
    print("DB:")
    for i in range(len(rows)):
            print(rows[i][0] + ':' + rows[i][1])
    return render_template('board1.html', rows = rows)

# 게시물 조회 (Read)
@app.route('/search', methods = ['GET', 'POST'])
def search():
    if request.method == 'POST':
        name = request.form['name']
        con = sqlite3.connect("database.db")
        cur = con.cursor()
        cur.execute(f"SELECT * FROM Board WHERE name='{name}' or context='{name}'")
        rows = cur.fetchall()
        print("DB:")
        for i in range(len(rows)):
            print(rows[i][0] + ':' + rows[i][1])
        return render_template('search.html', rows = rows)
    else:
        return render_template('search.html')

# 게시물 생성 (Create)
@app.route('/add', methods = ['GET', 'POST'])
def add():
    if request.method == 'POST':
        try:
            name = request.form['name']
            context = request.form['context']
            with sqlite3.connect("database.db") as con:
                cur = con.cursor()
                cur.execute(f"INSERT INTO Board (name, context) VALUES ('{name}', '{context}')")
                con.commit()
        except:
            con.rollback()
        finally : 
            con.close()
            return redirect(url_for('board'))
    else:
        return render_template('add.html')
# 위에 조회, 생성은 이전과 동일 

# 게시물 내용 갱신(Update)
@app.route("/update/<uid>", methods=["GET","POST"])
def update(uid):
    if request.method == "POST":
        name = request.form["name"]
        context = request.form["context"]
        
        # 내용 갱신하고
        with sqlite3.connect("database.db") as con:
            cur = con.cursor()  # connection한 db에 접근하기 위해, cursor 객체 만들기
            cur.execute(f"UPDATE Board SET name='{name}', context='{context}' WHERE name='{uid}'")
            con.commit()

        return redirect(url_for("board"))   # 갱신되었는지, board함수 리다이렉트해서, / 페이지 렌더링
    else:
        con = sqlite3.connect("database.db")
        cur = con.cursor()
        cur.execute(f"SELECT * FROM Board WHERE name='{uid}'")
        row = cur.fetchall()
        return render_template("update.html",row=row)

@app.route("/delete/<uid>")
def delete(uid):
    # 들어온 uid 값이랑 name이랑 delete 연산하고 반영
    with sqlite3.connect("database.db") as con:
        cur = con.cursor()
        cur.execute(f"DELETE FROM Board WHERE name='{uid}'")
        con.commit()

    return redirect(url_for('board'))  # 삭제 반영하고, 반영됬는지, board함수 리다이렉트, / 페이지 렌더링

if __name__ == '__main__':
    app.run()
<!-- ./templates/board1.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4><a href="{{url_for('add')}}">추가</a> <a href="{{url_for('search')}}">검색</a><br><br>목록</h4>
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
            <td>수정/삭제</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
            <td><a href="{{url_for('update', uid = row[0])}}">수정</a> <a
                    href="{{url_for('delete', uid = row[0])}}">삭제</a></td>
        </tr>
        {% endfor %}
    </table>
</body>

</html>
<!-- ./templates/add.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4>추가</h4>
    <form action="/add" method="POST">
        이름<br>
        <input type="text" name="name" /><br>
        내용<br>
        <input type="text" name="context" style="text-align:center; width:400px; height:100px;" /><br><br>
        <input type="submit" value="게 시" /><br>
    </form>
</body>

</html>
<!-- ./templates/research.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <form action="/search" method="POST">
        <input type="text" name="name" />
        <input type="submit" value="검 색" /><br>
    </form>
    <h4>검색결과</h4>
    {% if rows%}
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
        </tr>
        {% endfor %}
        {% else %}
        <p> 검색결과가 없습니다. </p>
        {% endif %}
    </table>
</body>

</html>
<!-- ./templates/update.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4>수정</h4>
    <form action="" method="POST">
        이름<br>
        <input type="text" name="name" /><br>
        내용<br>
        <input type="text" name="context" style="text-align:center; width:400px; height:100px;" /><br><br>
        <input type="submit" value="수 정" /><br>
    </form>
    <h4>기존</h4>
    <table border=1 width="600" align="center">
        <thead>
            <td>이름</td>
            <td>내용</td>
        </thead>
        <tr>
            <td>{{ row[0][0] }}</td>
            <td>{{ row[0][1] }}</td>
        </tr>
    </table>
</body>

</html>

[ 출처 : elice ]

 

728x90
반응형

📝 인증(Authentication)

인증은 유저의 identification을 확인하는 절차이다. 한 마디로, 유저의 아이디와 비밀번호를 확인하는 절차다.

인증을 하기 위해선 먼저, 유저의 아이디와 비밀번호를 생성할 수 잇는 기능도 필요하다.

우리가 생각하는 로그인 절차는 생각해보자.

1. 회원가입 : 아이디와 비밀번호를 생성한다.

  • 이때, 비밀번호는 암호화 저장한다.

2. 로그인 : 아이디와 비밀번호를 입력한다.

  • 입력한 비밀번호를 암호화 한 후, DB에 저장된 암호화 비밀번호와 비교한다.

3. 일치하면 로그인 성공, 일치하지 않으면 에러가 발생한다.

4. 로그인이 성공되면 access_tocken을 클라이언트에게 전송한다.

5. 유저는 로그인 성고 후 다음부터는 access_tocken을 첨부해서, request를 서버에 전송한다.

 

💡 비밀번호 암호화

유저의 비밀번호는 절대 그대로 DB에 저장되지 않는다. 만일 DB가 해킹 당할 경우, 그대로 유출되기 때문이다.
따라서, 유저의 비밀번호는 반드시 암호화하여 저장해야 한다. 그럴 경우 DB가 해킹 당하더라도 그대로 노출되지 않기 때문이다.

💡 허가(Authorization)

허가는 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차이다.
예를 들어 해당 유저는 고객 정보를 볼 수 있지만, 수정할 권한은 없는 경우이다.

 

📝 Flask 로그인 구현하기

# 1번을 해보세요!
from flask import Flask, request, render_template, session, url_for, redirect

app = Flask(__name__)
app.secret_key = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'
userinfo = {'Elice': '1q2w3e4r!!'}


@app.route("/")
def home():
    if session.get('logged_in'):
        return render_template('loggedin.html')
    else:
        return render_template('index.html')


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        name = request.form['username']
        password = request.form['password']
        try:
            if (name in userinfo):
                #2번을 해보세요!
                session["logged_in"] = True
                    #3번을 해보세요!
                return render_template('loggedin.html')
                    #4번을 해보세요!
            else:
                return '비밀번호가 틀립니다.'
            return '아이디가 없습니다.'
        except:
            return 'Dont login'
    else:
        return render_template('login.html')


@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        #4번을 해보세요!
        name = request.form['username'] 
        password = request.form['password']
        userinfo[name] = password
        
        return redirect(url_for('login'))
    else:
        return render_template('register.html')


@app.route("/logout")
def logout():
    session['logged_in'] = False
    return render_template('index.html')
    
if __name__ == '__main__':
    app.run()
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
        <p>로그인이 필요한 서비스입니다.</p>
        <a href= "login">로그인창으로</a><br>
        <a href= "register">회원가입</a>
</body>
</html>


<!-- loggedin.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
        <form action = "" method="post">
        <p>로그인 성공 페이지</p>
        <a href= "/logout">로그아웃</a>
        </form>
</body>
</html>


<!-- login.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
    <form action = "" method="post">
        <p>name : <input type="text" name = "username"></p>
        <p>password : <input type="password"  name = "password"></p>
        <p>이름과 비밀번호를 입력하고 로그인버튼을 누르세요.<br><input type = "submit" value = "로그인"/> </p>
    </form>
</body>
</html>


<!-- register.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
    <form action = "" method="post">
        <p>name : <input type="text" name = "username"></p>
        <p>password : <input type="password" name = "password"></p>
        <p>이름과 비밀번호를 입력하고 생성버튼을 누르세요.<br> <input type = "submit" value = "생성"/> </p>
    </form>
</body>
</html>

[ 출처 : elice ]

 

📝 로깅

로깅은 프로그램이 작동할 때 발생하는 이벤트를 추적하는 행위이다.
프로그램의 문제들을 파악하고 유지보수하는데 사용되고, 로깅을 통해 발생한 에러를 추적 가능하다.

운영 중인 웹 서비스에서 문제가 발생했을 경우, 해당 문제의 원인을 파악하려면 문제가 발생했을 때의 정보가 필요하다.
이런 정보를 얻기 위해서 중요한 기능의 실행되는 부분에 적절한 로그(log)를 남겨야 한다.
일반적인 로그 기록의 이점은 다음과 같다.

  • 로그는 성능에 관한 통계와 정보를 제공할 수 있다.
  • 로그는 재현하기 힘든 버그에 대한 유용한 정보를 제공할 수 있다.
  • 설정이 가능할 때, 로그는 예기치 못한 특정 문제들을 디버그하기 위해 그 문제들을 처리하도록  코드를  수정하여 다시 적용하지 않아도, 일반적인 정보를 저장할 수 있다.

💡 로깅 레벨(Loggin Level)

다음 순서로 로깅이 된다.

DEBUG < INFO < WARNING < ERROR < CRITICAL

  • DEBUG : 상세한 정보
  • INFO : 일반적인 정보
  • WARNING : 예상치 못하거나, 가까운 미래에 발생할 문제
  • ERROR : 에러 로그, 심각한 문제
  • CRITICAL : 프로그램 자체가 실행되지 않을 수 있는 문제

파이썬 로거 레벨 설정에 따라, 하위 레벨은 출력되지 않는다.
기본 로거 레벨 세팅은 WARNING 이다. 때문에  설정 없이는 INFO, DEBUG를 출력할 수 없다.

💡 python logger

기본적으로 다음 로깅 이력은 남기는 것이 좋다.

  • 서버 시작 로그
  • 서버 포트 번호
  • 함수 호출
  • 데이터의 입출력
#예시 코드
import logging 

if __name__ : '__main__': 
    logging.info("hello elice!")

위 코드로 실행하면, "hello elice!" 가 출력되지 않는다. 위에서 언급했듯이, 로깅의 기본 세팅은 WARNING이기 때문
그렇다면 어떻게 하면 "hello elice!" 가 출력이 될까 ?  아래 코드와 같이 설정하면 출력된다.

import logging 

if __name__ : '__main__': 
    logger = logging.getLogger() 
    logger.setlevel(logging.DEBUG) 	# 로깅 기본세팅 WARNING -> DEBUG로 변경
    logger.info("hello elice!")

 

📝 Flask logger

플라스크는 0.3 버전부터 logger를 플라스크 내부에서 제공하기 시작했다. 😮
(플라스크에서 기본적으로 제공하는 로깅을 제외하고, 일반 python logging을 사용해도 무방하다. )

# 레벨에 따른 함수가 이미 있어서, 따로 로깅레벨 설정을 안해도 된다.

from flask import Flask 
app = Flask(__name__) 
if __name__ == '__main__': 

    app.logger.info("test") 
    app.logger.debug("debug test") 
    app.logger.error("error test") 
    app.run()

 

💡 Flask logger 구현

from flask import Flask, render_template

app = Flask(__name__)

# .errorhandler(에러코드) : flask 내부에 기본적으로 있는 에러 핸들러
# 특정 에러에 대하여 errorhandler를 사용하면, 해당 에러가 발생했을 때 매칭된다.
@app.errorhandler(404)
def page_not_found(error):
    app.logger.error(error)
    return "페이지를 찾을 수 없습니다."

@app.route("/")
def hello_elice():
    return "Hello world!"

if __name__ == '__main__':
    app.run()

[ 출처 : elice ]

 

728x90
반응형

📝 렌더링 템플릿

렌더링(Rendering) : 마크업 언어를 브라우저(Client)에 보여주기 위한 과정

플라스크 내에서 html 파일을 사용해 웹 사이트를 제작할 수 있다. 이 때 사용하는 것이 렌더링 템플릿이다.
html 코드들을 플라스크내에 사용하는 것보다 파일을 따로 만들어 라우팅하는 방법이 유지보수에서 이롭다.

플라스크는 render_template( )을 이용해 html 파일을 렌더링하여 브라우저에 보여줄 수 있습니다.

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

hello 함수의 URL에 접근할 때, hello.html을 반환하며 해당 html 파일의 매개변수 name에 함수의 매개변수인 name을 전달한다.

 

📝 Jinja2

렌더링 템플릿을 통해 화면을 출력할 때 html에서는 아래와 같은 Jinja2 문법으로 해당 내용을 띄울 수 있습니다.

{% for row in rows %}
<tr>
    <td>{{ loop.index }}</td>
    <td>{{ row[0] }}</td>
    <td>{{ row[1] }}</td>
</tr>
{% endfor %}


<!--
	일반적인 형식
    
	{% if문 or for문 %}
    
    {{ 변수나 표현식}}
    	...	....
    {% endif 또는 endfor %}
-->

Docs : jinja.palletsprojects.com/en/2.11.x/templates/

 

Template Designer Documentation — Jinja Documentation (2.11.x)

This document describes the syntax and semantics of the template engine and will be most useful as reference to those creating Jinja templates. As the template engine is very flexible, the configuration from the application can be slightly different from t

jinja.palletsprojects.com

 

📝 블루 프린트

Flask의 요청으로 URL을 생성 할 때 화면을 출력하는 함수를 블루 프린트와 연결한다.

블루 프린트는 웹 애플리케이션의 개체를 미리 요구하지 않고 기능을 정의할 수 있다.
또한, 파일을 여러개로 나누어 유지 보수 측면에서 매우 효과적으로 개발할 수 있게끔 할 수 있다.

블루 프린트 없이 flask 웹 애플리케이션을 만들 수 있다.
하지만 여러 기능이 포함된 웹 애플리케이션을 제작하고자 한다면 블루프린트를 사용하는 것을 권장한다.

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template('pages/%s.html' % page)
    except TemplateNotFound:
        abort(404)

@simple_page.route 장식자를 이용해 함수를 바인딩하면 구현한 show() 함수를 통해 블루 프린트가 동작하게 된다.
만약 블루 프린트 등록하려면 아래와 같이 register_blueprint() 함수를 이용하면 된다.

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

 

📝 예제 ) 간단한 게시판 ( CRUD )

from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)
board = []

# 게시판 내용 조회 (Read)
@app.route('/')
def index():
    return render_template('list.html', rows=board)

# 게시판 내용 추가 (Create)
@app.route('/add',methods=["POST"])
def add():
    if request.method == "POST":
        name = request.form["name"]
        context = request.form["context"]
        board.append([name,context])
        return redirect(url_for("index"))
    else:
        return render_template("list.html", rows=board)

# 게시판 내용 갱신 (Update)
@app.route('/update/<int:uid>', methods=["GET","POST"])
def update(uid):
    if request.method == "POST":
        index = uid - 1
        name = request.form["name"]
        context = request.form["context"]
        
        board[index] = [name,context]   # 기존의 board내용에 덮어쓰기
        return redirect(url_for("index"))
    else:
        return render_template("update.html",index=uid,rows=board)

# 게시판 내용 삭제 (Delete)
@app.route('/delete/<int:uid>')
def delete(uid):
    index = uid - 1
    del board[index]

    return redirect(url_for("index"))


if __name__ == '__main__':
    app.run(debug=True)
<!-- ./template/list.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4>추가</h4>
    <form action="/add" method="POST">
        이름<br>
        <input type="text" name="name" /><br>
        내용<br>
        <input type="text" name="context" style="text-align:center; width:400px; height:100px;" /><br><br>
        <input type="submit" value="게 시" /><br>
    </form>
    <h4>목록보기</h4>
    <table border=1 width="600" align="center">
        <thead>
            <td>목차</td>
            <td>이름</td>
            <td>내용</td>
            <td>수정, 삭제</td>
        </thead>
        {% for row in rows %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
            <td><a href="{{url_for('update', uid = loop.index)}}">수정</a> <a
                    href="{{url_for('delete', uid = loop.index)}}">삭제</a></td>
        </tr>
        {% endfor %}
    </table>
</body>

</html>
<!-- ./templates/update.html -->

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

<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>SQLite3 게시판 등록</title>

    <style type="text/css">
        body {
            text-align: center;
        }
    </style>
</head>

<body>
    <h3>게시판</h3>
    <h4>수정</h4>
    <form action="/update/{{index}}" method="POST">
        이름<br>
        <input type="text" name="name" /><br>
        내용<br>
        <input type="text" name="context" style="text-align:center; width:400px; height:100px;" /><br><br>
        <input type="submit" value="수 정" /><br>
    </form>
    <h4>기존</h4>
    <table border=1 width="600" align="center">
        <thead>
            <td>목차</td>
            <td>이름</td>
            <td>내용</td>
        </thead>
        <tr>
            <td>{{ index}}</td>
            <td>{{ rows[index-1][0] }}</td>
            <td>{{ rows[index-1][1] }}</td>
        </tr>
    </table>
</body>

</html>

[ 출처 : elice ]

 

728x90
반응형

📝 REST란 무엇인가 ?

REST : Representational State Transfer

= 자원을 이름(자원의 표현)으로 구분하여 해당 자원의 상태를 주고 받는 것을 의미한다.
= 즉, 자원(resourece) 의 표현(representation)에 의한 "상태 전달"

  • 자원의 표현
    • 자원 : 해당 소프트웨어가 관리하는 모든 것 ( ex. 그림, 문서, 데이터 등 )
    • 자원의 표현 : 그 자원을 표현하기 위한 이름
  • 상태(정보) 전달
    • 데이터가 요청되는 시점에서 자원의 상태(정보)를 전달한다.
    • JSON 혹은 XML을 통해 데이터를 주고 받는 것이 보통의 전달 방식이다.

WWW(World Wide Web) 와 같은 시스템을 위한 소프트웨어 개발 아키텍처의 한 형식이다.

REST는 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 사용하기 때문에, 웹의 장점을 최대한 활용할 수 있는
아키텍처 스타일이다. REST는 네트워크 상에서 클라이언트와 서버 사이의 통신 방식 중 하나이다.

 

💡 REST의 구체적인 개념

HTTP URI(Uniform Resource Identifier)을 통해 자원을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.

  • REST는 자원 기반의 구조(ROA) 설계의 중심에 자원이 있고, HTTP Method를 통해 자원을 처리하도록 설계된 아키텍쳐를 의미한다.

💡 CRUD

  • Create : 생성(POST)
  • Read : 조회(GET)
  • Update : 갱신(PUT)
  • Delete : 삭제(DELETE)
  • HEAD : header 정보 조회(HEAD)

💡 REST 장단점

장점

  • 서버와 클라이언트의 역할을 명확하게 분리한다.
  • 여러가지 서비스 디자인에서 생길 수 있는 문제를 최소화한다.
  • REST API 메시지가 의도하는 바를 명확하게 나타내므로, 의도하는 바를 쉽게 파악할 수 있다.
  • HTTP 표준 프로토콜에 따르는 모든 플랫폼에서 사용이 가능하다.
  • HTTP 표준 프로토콜의 표준을 최대한 활용하여 여러 추가적인 장점을 함께 가져갈 수 있게 해준다.

단점

  • 사용할 수 있는 메소드가 4가지 밖에 없다. (POST,GET,PUT,DELETE)
  • 표준이 존재하지 않는다.

💡 그럼에도, REST가 왜 필요한가 ?

  • 다향한 클라이언트의 등장
  • 애플리케이션 분리 및 통합

최근 서버 프로그램은 다양한 브라우저(chrome, Explorer, safari 등)와 안드로이드폰, 아이폰과 같은 모바일 디바이스에서도 통신할 수 있어야 하기 때문이다.

💡 REST 구성요소

  • 자원(Resource) : URI
    • 웹 서버의 자원은 각자 이름을 가지고 있다.
    • 따라서, 클라이언트는 이러한 이름을 통해 원하는 정보를 찾을 수 있다.
    • 이때 서버 자원의 이름을 URI라고 한다.
  • 행위(Verb) : HTTP Method
    • HTTP 프로토콜은 GET,POST,PUT,DELETE와 같은 메서드를 제공한다.
  • 표현(Representation of Resource)
    • 클라이언트가 자원의 상태(정보)에 대해 요청하면, 서버는 이에 적절한 응답을 보내야한다.
    • REST의 하나의 자원은 JSON, XML, TEXT, RSS 등 여러 형태의 응답으로 나타낼 수 있다.
    • 하지만, 보통 JSON 이나 XML을 통해 데이터를 주고 받는 것이 일반적이다.

 

📝 HTTP Method : [ POST ]

HTTP 메소드 중 POST는 요청을 처리하기 위해 사용자(클라이언트)로부터 특정 양식(form)의 "데이터를 암호화"하여 서버로 전송하는 방법이다.

서버는 POST 방식으로 전달 받은 데이터를 통해 특정 동작을 수행할 수 있다.
예로 로그인 예시를 보자.

<!-- login.html -->
<html>  
   <body>  
      <form action = "/login" method = "post">  
         <table>  
        <tr><td>아이디</td>  
        <td><input type ="text" name ="id"></td></tr>  
        <tr><td>비밀번호</td>  
        <td><input type ="password" name ="pass"></td></tr>  
        <tr><td><input type = "submit"></td></tr>  
    </table>  
      </form>  
   </body>  
</html>
# login.py

from flask import *  
# Flask 인스턴스 생성
app = Flask(__name__) 


@app.route('/')
def hello():
    return render_template('login.html')


# login 주소에서 POST 방식의 요청을 받았을 때
@app.route('/login',methods = ['POST'])  
def login():  
    id = request.form['id']  
    passwrd = request.form['pass'] 

    if id=="Elice" and passwrd=="Awesome!": 
        return "Welcome %s" % id  


if __name__ == '__main__':  
    app.run()

[ 출처 : elice ]

이는, login.html 에서 "아이디"와 "비밀번호"를 입력하고, "버튼을 누르면" login.py 에서 이를 인식해서
"Elice" 이고, 비밀번호가 "Awesome!" 이면, "Welcome Elice" 라는 문구가 나타난다.

 

📝 HTTP Method : [ GET ]

GET 요청 또한 사용자(클라이언트)로부터 특정 양식(form)의 데이터를 서버로 전송하는 방법이다.

가장 큰 차이는 정보를 URL에 붙여서 보내게 되고, 즉, "데이터가 암호화되지 않는다는 점"이다.

앞선 로그인 예시를 통해 보자.

<!-- login.html -->
<html>  
   <body>  
      <form action = "/login" method = "get">  
         <table>  
        <tr><td>아이디</td>  
        <td><input type ="text" name ="id"></td></tr>  
        <tr><td>비밀번호</td>  
        <td><input type ="password" name ="pass"></td></tr>  
        <tr><td><input type = "submit"></td></tr>  
    </table>  
      </form>  
   </body>  
</html>
# login.py

from flask import *  
# Flask 인스턴스 생성
app = Flask(__name__)  


# login 주소에서 GET 방식의 요청을 받았을 때
# @app.route()에서 methods 인자의 기본 값은 GET으로 생략해서 작성해도 됩니다.
@app.route('/login',methods = ['GET'])  
def login():  
      id = request.args.get['id']  
      passwrd = request.args.get['pass']  
      if id=="Elice" and passwrd=="Awesome!": 
          return "Welcome %s" % id  


if __name__ == '__main__':  
   app.run()

[ 출처 : elice ]

 

GET이 POST 방식과 가장 큰 차이점은 URL에 "https://localhost:8080/login?id=elice&pass=awesome" 으로 표현된다.

즉, 로그인 할 때, URL에 아이디와 비밀번호가 표기된다. ( = 데이터가 암호화가 되지 않는다. )
이는, 한편으로 정보보안을 유지할 수 없다는 것을 의미한다.

따라서, "로그인"과 같이 정보를 가려야할 때는 GET이 아닌 POST 방식을 사용하는 것이 맞다.

 

📝 HTTP - POST 예제

POST 요청은 눈에 파라미터가 보이는 GET요청과 달리 전달하려는 정보가 HTTP body에 포함되어 전달된다.

전달하려는 정보는 form data, json strings 등이 있다.

render_templates( ) 메소드 = Flask 에서 html 파일을 "랜더링" 하기 위해서 사용한다.
( 랜더링(Rendering) = 웹 브라우저 상의 화면을 표현하는 과정 )

POST로 전달받은 "값"은 request.form[ '변수명' ]을 사용하면 html의 form에서 해당 변수명에 만족 값을 사용가능

from flask import Flask
from flask import request
from flask import render_template

app = Flask(__name__)

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


@app.route("/post", methods=['POST'])
def post():
    #1번을 해보세요!
    value = request.form["input"]
    msg = "%s 님 환영합니다." % value
    return msg


if __name__ == '__main__':
    app.run()
<!-- 사용하는 html 파일들은, 반드시 templates 디렉터리 안에 있어야한다. !! -->
<!-- /templates/index.html -->


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
    <form action = "/post" method="post">
        <p>
            이름 : 
            <input type="text" name = "input">
        </p>
        <p>
            이름을 입력하고 제출버튼을 누르세요.<br>
            <input type = "submit" value = "제출" onclick = "alert('제출 완료!')" />
        </p>
    </form>
</body>
</html>

아이디 입력란에 아이디를 입력하고 제출하면, 제출완료 ! 와 동시에
post 방식으로, input에 "id님 환영합니다." 출력 

 

📝 HTTP - GET 예제

Flask 에서 GET 또는 POST로 전달받은 정보를 request 모듈을 사용하여 활용할 수 있다.

GET 방식의 경우 모든 파라미터를 URL로 보내 요청하는 방식이라고 했다.

request 모듈에는 GET 방식으로 URL의 인자를 'key = value' 형태로 전달했을 때, 다음 방식으로 활용한다.

  • 다음과 예시와 같은 주소를 입력했을 시, number의 값은 = 1 (https://주소.com/?key=1)
number = request.args.get('key`, 초기값)

초기값은 key 값으로 아무 값도 넘겨받지 못했을 때 활용되는 값이다. 이후에는 URL을 통해 '/?key=value' 형태로
key값을 전달받으면 해당 값을 사용한다.

여러개의 key와 값을 전달받으려면 다음 예시와 같이 '&' 기호를 입력하여 추가할 수 있다. 

  • key의 값은 1, value의 값은 2 (https://주소.com/?key=1&value=2)
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route('/')     # Default는  HTTP methods = ['GET ']
def user_juso():
    temp1 = request.args.get("word1","Elice")   # GET 방식으로, request.args.get() 할때, 
    temp2 = request.args.get("word2","Hello")   # 주소에 값이 지정안되면 기본값은, 다음과 같음을 의미
    return temp1 + "<br>" + temp2

if __name__ == '__main__':
    app.run()

GET 방식 -> URL에 값을 전달할 경우
      GET -> URL 에 값이 전달되지 않을 경우        = default 값으로 대체

 

📝 HTTP - GET & POST 예제

GET과 POST를 동시에 사용해서 웹 페이지를 동작 시킬 수 있다.

method가 GET이면 이름을 입력받는 웹 페이지를 동작시키고, POST라면 입력 같은 값을 포함하는 문장을 출력한다.

<!-- templates/index2.html -->

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HTML for python flask</title>
</head>

<body>
    <form action = "/" method="POST">
        <p>
            이름 :
            <input type="text" id = "input" name = "input">
        </p>
        <p>
            이름을 입력하고 제출버튼을 누르세요.
            <input type = "submit" value = "제출" onclick = "alert('제출 완료!')" />
        </p>
    </form>
</body>
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/', methods=["GET","POST"]) # GET 또는 POST가 호출될 수 있다는 것
def post():
    if request.method == "POST":
        value = request.form["input"]   # index2.html가서 form태크를 보면 요소에 method = "POST" 일것이다.
        return f"{value}님 환영합니다."
    
    if request.method == "GET":
        return render_template("index2.html")

if __name__ == '__main__':
    app.run()

GET 
form 입력, 즉 POST로 데이터를 보내겠다.
POST

 

📝 API 와 End point가 무엇인가 ?

API프로그램들이 서로 상호작용하는 것을 도와주는 매체이다.

우리가 음식점에 갔을 때, 아래와 같이 행동한다. 점원은 손님에게 메뉴를 알려주고, 주방에 주문받은 요리를 요청한다.
그 다음 주방에서 완성된 요리를 손님에게 다시 전달한다.

  • 손님과 요리사는 "End Point"
  • 점원은 이 둘 사이에서 "API"

[ 출처 : elice ]

API는 손님(프로그램)이 주문할 수 있게 메뉴(명령 목록)를 정리하고, 주문(명령)을 받으면 요리사(응용프로그램)와 상호작용하여 요청된 메뉴(명령에 대한 값)를 전달한다.

 

💡 API의 역할은 무엇인가 ?

1. API는 서버와 데이터베이스에 대한 출입구 역할을 한다.

  • 데이터베이스에는 정보들이 저장된다. 
  • 따라서, 모든 사람들이 이 데이터베이스에 접근할 수 있으면 안된다.
  • API는 이를 방지하기 위해 우리가 가진 서버와 데이터베이스에 대한 출입구 역할을 하며, 허용된 사람들에게만 접근성을 부여해준다.

2. API는 프로그램과 기기가 원활하게 통신할 수 있도록 한다.

  • 우리가 흔히 알고 사용하고 있는 스마트폰 어플이나 프로그램과 기기가 데이터를 원활히 주고 받을 수 있도록 돕는 역할을 한다.

3. API는 모든 접속을 표준화한다.

  • API는 모든 접속을 표준화하기 때문에 기기나 운영체제 등과 상관없이 누구나 동일한 권한을 얻을 수 있다.

 

💡 API Testing

API Testing 은 API를 테스트하여 기능, 성능, 신뢰성, 보안 측면에서 기대를 충족하는지 확인하는 테스팅의 한 유형

완벽하게 작동하는 API만이 실제 애플리케이션에서 사용할 수 있다.
따라서, 정상적으로 완벽하게 작동하는지 테스트 해야한다.
API 테스트를 진행하게 되면 향후 특정 시점에서 발생할 수 있는 애플리케이션의 많은 문제점들을 해결할 수 있다.

[ 출처 : elice ]

728x90
반응형

📝 Flask

플라스크는 많은 사람이 "Micro Web Framework" 라고 부른다.
여기서 "Micro" = "프레임워크를 간결하게 유지하고 확장할 수 있도록 만들었다" 라는 의미

  • Micro : 가벼운 기능 제공, 가볍게 배우고 가볍게 사용할 수 있으며, 확장성이 넓다.
  • Framework : 이미 작성된 코드의 모임인 "라이브러리" 그 이상의 의미, 어플리케이션을 개발하기 위한 일정한 "뼈대"를 제공해주는 기술

간단한 웹 사이트, 간단한 API 서버를 만드는 데에 특화 되어있는 Python Web Framework 이다.
요즘은 클라우드 컴퓨팅의 발달로 Docker, Kubernetes와 접목해서, 소규모 컨테이너 단위로 기능 별 개발 후, 한꺼번에 배포하는 방식이나 배포 후 기능을 추가하는 식으로 자주 사용하고 있다.

💡 플라스크 장점

  • 가볍게 배울 수 있다 : HTML/CSS/JS 알고, python을 어느정도 알고있다면, 빠르게 배울 수 있다.
  • 가볍게 사용 가능하다. : 코드 몇 줄이면, 만들 수 있다.
  • 가볍게 배포할 수 있다. : pip을 사용하여 Flask를 다운받고 배포하면 끝이다.

💡 플라스크의 단점

  • Django에 비해서 자유도가 높으나, 제공해주는 기능이 덜 하다.
  • 복잡한 어플리케이션을 만들려고 할 때, 해야 할 것들이 많다.

Flask는 소규모의 어플리케이션을 빠르게 만들 수 있고, 배포 환경에 따라 대규모 어플리케이션의 기능 확장의 역할을 하기 쉬운 장점이 있다.
반면, Django는 대규모 어플리케이션을 빠르게 만들 수 있으며, 기본으로 제공해 주는 기능이 많다.

Flask Django
ORM 기능이 제공되지 않음 ORM 기능이 내장
짧은 코드로 웹 서버 구동가능 자동으로 관리자 화면 구성

ORM(Object Relational Mapping) : ORM은 DB와 OOP 언어 간의 호환되지 않는 데이터를 변환하는 프로그래밍 기법

 

📝 로컬에서 Simple Web Server띄우기 : Hello World !!

환경 : VS code, Python.exe 20.3.3 version 이상
(오류 발생시, 보통, 파이썬 실행파일 버젼이, 이전 버젼이어서 발생할 수 있으니 그럴 경우, 버젼 업그레이드 필요)

c:/python39/python.exe -m pip install --upgrade pip
c:/python39/python.exe -m pip install --upgrade pip --user (액세스 거부시)

성공 시, 아래와 같은 "Hello world!"를 보여주는 간단한, 플라스크 코드 작성

from flask import Flask  # 플라스크 import

app = Flask(__name__)   # app 이라는 이름의, flask앱을 하나 만들꺼야.

@app.route('/')             # 라우팅, '/'는 접속시, root 경로
def hello_World():          # '/'과 매칭되는 함수(root 경로일 시), 페이지에서 작동할 메소드
    return "Hello World!"

if __name__ == '__main__':  # 모듈 명이 "main"일 때만 실행하도록 조건문
    app.run()

127.0.0.1:5000(로컬) 에 출력된 Hello World

 

📝 Flask  HTTP 통신 : HTTP Methods

먼저 HTTP 개념을 모른다면 아래, 포스팅을 잠깐 읽고 오자. 

 

[Network] HTTP란 ?

📝 개요 우리 모두, 대부분 정보를 인터넷으로 확인한다. 모든 "웹 브라우저"에 있는 정보에 접근할 때는, "URL"을 통하여 접근한다. 반대로 생각하면, "URL을 모르는 정보에는 접근을 할 수 없다"

youngminieo1005.tistory.com

Flask는 HTTP 통신을 위해 다음과 같은 Methods를 제공한다.

Method 설명
GET 암호화되지 않은 형태의 데이터를 서버로 전송하는데 사용되는 가장 일반적인 방법
HEAD GET과 유사한 방법으로 Response Body를 포함지 않고 사용
POST 특정 양식의 데이터를 암호화하여 서버로 전송하는데 사용
PUT 특정 대상의 데이터를 갱신(Update)하는데 사용
DELETE URL에 지정된 대상을 삭제(DELETE)하는데 사용

 

📝 URL Routing 이란 ?

URL Routing : 서버에서 특정 URL에 접속했을 시, 보여줄 페이지를 정해주는 것

즉, 특정 URL을 일부 작업을 수행하기 위한 관련된 기능과 "Mapping"하는 데 사용된다.

https://page.com/home  # 홈화면을 보여주고
https://page.com/login # 로그인화면을 보여준다.

위에 예시같은 것이, URL Routing 이다.

💡 Flask에서 URL Mapping을 어떻게 할까 ?

Flask에서 URL Routing은 @app.route(' ')를 통해 수행한다.
또한, 여기서 @를 통해 선언하는 방식 = 데코레이터(decorator)라고 부른다.

from flask import Flask  # 플라스크 import

app = Flask(__name__)   # app 이라는 이름의, flask앱을 하나 만들꺼야.

@app.route('/')             # 라우팅, '/'는 접속시, root 경로
def home():          # '/'과 매칭되는 함수(root 경로일 시), 페이지에서 작동할 메소드
    return "This is Home page !!"

@app.route('/hello')        # "/hello" URL 접속 시 
def hello():
    return "Hello world !!"
if __name__ == '__main__':  # 모듈 명이 "main"일 때만 실행하도록 조건문
    app.run()

localhost 페이지
localhost/hello 페이지

 

📝 Variable Rules

플라스크에서는 route( ) 를 사용해서 url path의 값을 활용할 수 있다.
url path의 문자열, 정수, 서브경로를 활용할 수 있다.

  • 문자열은 사용할 변수명을 <변수명>으로 감싸주면 함수에서 전달받은 값을 활용할 수 있다.
  • 정수는 <int:변수명> 형태로 사용이 가능하다.
  • 서브경로는 <path:subpath> 형태로 사용이 가능하다.
from flask import Flask

app = Flask(__name__)

@app.route('/user/<username>')
def show_user_profile(username):
    return f"User {username}"

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f"Post {post_id}"

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    return f"Subpath {subpath}"

if __name__ == '__main__':
    app.run()

문자열  = <변수명>

 

정수 = <int:변수명>
서브경로 = <path:서브경로>

 

📝 데이터 반환하기 - jsonify( )

Flask 에서는 데이터를 json 파일 형식으로 데이터를 교환한다.

json 파일 형식은 웹 사이트 상에서 정보를 주고 받는 형식이다. Dictionary형태와 유사한 구조를 가진다.

Flask 에는 데이터를 json 형식으로 바꿔주는 jsonify( ) 메소드가 있다.

from flask import Flask, jsonify    # jsonify import

app = Flask(__name__)

@app.route('/')
def json():
    people = [
        {'name' : 'natto' , 'birth-year' : 2020},
        {'name' : 'apple' , 'birth-year' : 2021},
        {'name' : 'banana' , 'birth-year' : 2022}
    ]
    return jsonify(people)

if __name__ == '__main__':
    app.run()

jsonify( ) 반환 = json형식

 

📝 URL 설계하기 - URL Building

웹 사이트의 기본 구조를 설계하고 사용자들이 웹 사이트에 접속하기 위해 URL 구조를 설계할 필요성이 있다.

만약 한 페이지에서 다른 페이지로 이동할 수 있도록 "링크"를 생성하려면 어떻게 해야 할까 ?

Flask 에서는 url_for( ) 메소드를 통해 이를 수행한다.

url_for( ) 메소드 : 특정 함수에 대한 url을 "동적"으로 구축하는 데 사용한다.
일반적으로 url 끝점에 이동할 함수 이름과 점(.)을 접두사로 붙이는 것처럼 사용할 수 있다.

from flask import Flask, redirect, url_for

app = Flask(__name__)

# 홈 페이지로 이동
@app.route('/')
def home():
    return "주소창에 /user/admin과 /admin <br> /user/student 와 /student를 입력해보세요."

# 관리자 페이지로 이동
@app.route('/admin')
def admin():
    return "관리자 페이지 입니다."

@app.route('/student')
def student():
    return "학생용 페이지 입니다."

# redirect() 는 "페이지에 다시 연결한다"는 뜻으로 마치 페이지를 "새로고침" 한 것과 같은 동작한다.
@app.route('/user/<name>')
def user(name):
    # 전달 받은 name이 'admin'이라면 ?
    if name == 'admin':
        return redirect(url_for('admin'))
    
    # 전달 받은 name이 'student' 라면 ?
    if name == 'student':
        return redirect(url_for('student'))


if __name__ == '__main__':
    app.run()

/admin = /user/admin 같은 페이지를 보여준다.
/student = /user/studnet 같은 페이지를 보여준다.

다만, 차이는 터미널에서 볼 수 있듯이, "HTTP status code" 인 것 같다.

위에, HTTP 포스팅에서 설명했듯이

300 = "리다이렉트" , 200 = "성공" 을 의미한다.
두 경우 모두, GET Method를 사용한것은 공통점이다.

728x90
반응형

📢 개요

정규표현식(Regular Expressions) : 복잡한 문자열을 처리할 때 사용하는 도구

  • 특정 프로그래밍 언어에 종속된 문법을 가진 것이 아니라, 문자열을 처리하는 곳이라면 폭넓게 사용 가능
  • 파이썬의 기본 모듈 중 re 모듈이 정규표현식을 지원

👇 왜 배워야 할까 ?

더보기

다음과 같은 문제가 주어졌다고 생각해보자.
[ 보안을 위해 고객 정보 중 전화번호 가운데 자리의 숫자는 * 문자로 변경하세요. ]

고객 정보는 이름, 주민번호, 전화번호가 문자열 데이터로 주어진다고 가정해봅시다.

text = '''
Elice 123456-1234567 010-1234-5678
Cheshire 345678-678901 01098765432
'''

이 문제를 정규식을 사용하지 않고 풀려면 매우 복잡하게 풀어야 합니다.

  1. 전체 텍스트를 공백 문자를 기준으로 나눈다.
  2. 나누어진 문자열이 전화번호 형식인지 점검한다.
  3. 전화번호를 다시 나누어 가운데 자리의 숫자를 *로 변환한다.
  4. 나눈 문자열을 다시 합쳐 전화번호를 완성한다.

그런데 나누어진 문자열을 전화번호 형식인지 어떻게 점검할까요? 그리고 가운데 자리의 숫자는 어떻게 나누어 *로 변환하고 다시 합칠까요?

설상가상으로 전화번호의 구분문자인 -도 있기 때문에 문제 해결은 더욱 쉽지 않습니다.
그러나 정규표현식을 이용하면 매우 간편하게 코드를 작성할 수 있습니다.

앞서 예로 든 전화번호같이, 원하는 형식의 문자열을 검색할 때 메타문자와 수량자 등 다양한 패턴을 사용하여 매치하고, 그룹핑을 이용하여 원하는 부분만 골라내고 re모듈의 메서드로 문자열을 수정할 수도 있습니다.

 

📢 re 모듈

import re	
 

re — Regular expression operations — Python 3.9.1 documentation

This module provides regular expression matching operations similar to those found in Perl. Both patterns and strings to be searched can be Unicode strings (str) as well as 8-bit strings (bytes). However, Unicode strings and 8-bit strings cannot be mixed:

docs.python.org

 

📢 정규식 검사 함수

문자열에 대해 정규식으로 검사하는 함수는 대표적으로 4가지가 있다.

함수 이름 기능
re.match(pattern, string) string 시작 부분부터 패턴이 존재하는지 검사하여 MatchObject를 반환함.
re.search(pattern, string) string 전체에서 pattern이 존재하는지 검사하여 MatchObject를 반환함.
re.findall(pattern, string) string 전체에서 패턴과 매치되는 모든 경우를 찾아 list로 반환함.
re.finditer(pattern, string) string 전체에서 패턴과 일치하는 결과에 대한 iterater 객체를 반환함.

 

📢 문자열 수정 함수

re 모듈에는 패턴과 매치된 무자열을 찾아줄 뿐만 아니라, 편집할 수 있는 함수들도 존재한다.

함수 이름 기능
re.sub(pattern, repl, string) string 에서 pattern과 매칭되는 부분을 repl로 수정한 문자열을 반환함.
re.subn(pattern, repl, string) re.sub()과 동일하지만, 함수의 결과를 (결과 문자열, 교체 횟수)꼴의 튜플로 반환함.

 

📢 메타 문자

메타 문자특정한 문자 혹은 계열을 표현하는 약속된 "기호" 이다.
메타 문자를 이용하면 특정한 규칙을 가진 여러 단어를 짧게 압출할 수 있어 편리하다.

주요 메타문자

  • [^ ] 와 같이 문자클래스 [ ] 안에 ^이 쓰이면, 문자열의 시작이 아닌, not 에 의미로 사용
  • [ - ]문자클래스 [ ] 안에 - 를 기준으로, 왼쪽 오른쪽에 범위로 쓸 문자를 쓰면, 그 사이에 범위에 해당하는 문자 전부를 의미한다. 반드시 왼쪽의 유니코드(또는 아스키코드)값이 오른쪽보다 작아야한다.
  • ★ "(?i)" = "(?i) 플래그" 라고 부른다. 특정 패턴 위치에 상관없이, 이 플래그를 넣어주면, 대소문자를 구분하지 않겠다는 의미

 

📢 수량자(Quantifier)

동일한 글자나 패턴이 반복될 때, 그대로 정규표현식을 만들고자 하면 상당히 불편하다.
\d 와 \w를 이용하면 각각 숫자와 문자를 "한 글자"씩 매칭해준다.
이는, 이어지는 문자를 패턴으로 만들어, "단어 단위"로 매칭해야 할 때 매우 불편하다.

이런 상황에서 유용한게 수량자(Quantifier) 이다.

주요 수량자(Quantifier)

  • ★ ? = "수량자 탐욕성 억제" 연산자

    표에, "0개 또는 1개"를 의미하는 수량연산자 ? 와는 다른 표현이다 !! (주의)
  • 수량자의 원래 특성은 "탐욕적"이라서, 매칭되는 "최대한 긴 문자열을 반환"하는데, 그게 아니라 가능한 "최소한"의 수량과 매칭하고 싶을 때 사용한다.

    ex) text = "<html><head><title>제목</head></html>" 일 때


    1. pattern = "<.*>" 일 때

    👉 ["<html><head><title>제목</head></html>"]  전체 하나가 반환 될것이다. <의 시작과 >의 맨끝을 기준

    2. pattern = <.*?>" 일 때 ( 수량자 탐욕 억제 사용 )

    👉 [ "<html>" , "<head>" , "<title>" , "</head>", "</html>" ] 반환. < >의 최소한의 기준으로 나눈다.

 

📢 그룹

( )그룹을 나타낸다. 그룹은 전체 패턴 내에서 "하나로 묶여지는 패턴"을 말한다.
그룹과 | 를 결합한 형태, 또는 그룹 뒤에 수량자를 붙이는 패턴으로 자주 사용된다.

예시

  • (e|a)lice  elice, alice와 매칭됩니다.
  • (tom|pot)ato는 tomato, potato와 매칭됩니다.
  • (base|kick){2}  basebase, basekick, kickkick, kickbase 와 매칭됩니다.

그룹 참조 예시

  • 특정문자열 = "(그룹1)쏼라쏼라~(그룹2)"  << 예시가 있다치자.
  • ★ 이 그룹을 "참조"할 때, "\g<그룹번호>" 를 사용하여, 내가 정의했던, 정규식의 그룹을 참조할 수 있다.

그룹의 재사용

한 번 만든 그룹은 재사용할 수도 있습니다. 만들어진 순서부터 1번부터 시작하는 그룹으로 참조할 수 있는데, 매치한 그룹을 패턴 내에서 재사용하려면 \\1과 같이 그룹 번호를 이스케이프하여 나타내야 합니다.

예시

  • (to)ma\\1은 tomato와 매칭됩니다. 괄호를 사용하여 앞에서 만든 그룹 (to)를 뒤에서 재사용하는 모습입니다.

외 ( group( ) 메소드 )

이외에도 그룹에는 re 모듈의 match 객체에 속해있는 group 메서드를 이용하여 매칭된 결과 중 일부 내용만을 추출할 수 있는 등 다양한 사용법이 있습니다.

text = "목표텍스트"
pattern = "정규표현식"
match_object = re.search(pattern, text)		# 매칭된 "객체"를 반환
match_object.group()	# 매칭된 객체 기반으로, 그루핑

 

📢 비캡쳐링그룹

예를들어, "tomato potato" 라는 문자열이 있다.  여기서, tomato 와 potato를 뽑아내기위해 앞선, "그룹"으로는"(tom|pot)ato"를 패턴으로 넣을 것이다. 

👉 그러나, 결과는 ["tom", "pot"] 으로 나올 것이다.

★ 이유는, 파이썬 정규표현식에서 그룹으로 "캡쳐" 한 부분이 있다면, "이외에 부분들은 출력하지 않기 때문"이다.
위 패턴에서는, "ato"는 그룹에 속해있지 않기 때문에, findall 함수에서 누락(해당되지 X)된 것이다.

해결법

👉 그룹 ( ) 앞에 ?: 을 붙인다.  ( ex. "(?:tom|pot)ato)" )

단, 비캡쳐링 그룹은 캡쳐가 되지 않는 그룹이기 때문에, "참조하여 사용할 수 없다."

비캡쳐링그룹은 패턴 문자들을 묶되, 그룹 단위로 매칭되지는 않게끔 한다.
그룹으로 묶은 것들을 "최종 결과"에서 따로 구분하여 사용하지 않는 경우에 적용한다.
 

 

📝 Reference : Elice Academy

728x90
반응형

📢 개요

코딩테스트를 준비하면서, 파이썬으로 코드를 구현할 일이 많이 생겼다.

근데, 얼마가 지나지 않아서, 같이 공부하는, 파이썬으로 구현을 하는 몇몇 사람들, 코드를 보다가..

예를들어, 두 수의 합과 곱을 구하는 코드를 구현한다고 치면

  • case.1
n,m = map(int,input().split())

print(n + m)
print(n * m)
  • case.2
def sum(self,a,b):
	return a + b
def mul(self,a,b):
	return a * b

if __name__ == '__main__' :
	n,m = map(int,input().split())
	print(sum(n,m))
    print(mul(n,m))

위와 같이, 두가지 케이스로 구분됐다.

case.1이 파이썬으로 갈아탄지 얼마안된, 내 코드구현방식
case.2가 몇몇 분들의 코드구현방식

결론만, 먼저 정리하면, case.2 방식으로, 앞으로 선호하는것이 이상적(?)이다.
(물론, 위에 예시는, 아주~~간단한 예시다보니, case.1방식이 "더 보기에도, 간단해 보이지않느냐" 할 수 있지만, 그것의 문제가 아니다.

작은 기능을 하는 프로그램이라도, 각각을 메소드화 시키고, main(메인)에서 해당 로직이 실행되게 작성하는 습관을 들여야 한다.

후에, 코딩테스트용이 아니고, 프로젝트개념으로, 접근을 하게 되면, 우리는 또한,
스크립트파일로 실행하는 방식 VS 모듈로 사용하는 방식을 구분해서 알고 있어야한다.

📢 if __name__ == '__main__' :

일단, 그럼 다시 주제로 돌아와보자.

if __name__ == '__main__':

파이썬 코드들을 보면, 여러 메소드나, 클래스가 구현되어있고, 마지막에는, 위에 코드가 항상 보이는데, 이게 뭘까.

👉 이 코드의 의미는, 현재 스크립트 파일이 실행되는 상태를 파악하기 위해 사용

예를 들어보자.

📝 hello.py

print('hello 모듈 시작')
print('hello.py __name__:', __name__)    # __name__ 변수 출력
print('hello 모듈 끝')

📝 main.py

import hello    # hello 모듈을 가져옴
 
print('main.py __name__:', __name__)    # __name__ 변수 출력

📝 실행결과

hello 모듈 시작
hello.py __name__: hello
hello 모듈 끝
main.py __name__: __main__

👉 실행을 해보면, hello.py 파일과, main.py파일의 __name__ 에 변수 값이 출력됩니다.

파이썬에서 import로 모듈을 가져오면, 해당 스크립트 파일이 한 번 실행이 됩니다.
위에 예시를 보면, hello모듈을 가져오면, hello.py안에 코드가 실행되고, 따라서, hello.py의 __name__ 변수에는 'hello'가 들어가고, main.py의 __name__ 변수에는 '__main__' 이 들어가는 것이고, 이를 출력으로 확인 할 수 있죠 ?

이런 상황입니다.

즉, __name__ = 모듈의 이름이 저장되는 변수이고

import로 모듈을 가져왔을 때, 모듈의 이름이 들어가는 것이죠.

"파이썬 인터프리터"로 스크립트 파일을 직접 실행했을 때도 (근데,,python도 스크립트 언어입니다.) 역시 같은 결과를 보입니다. 이것은, 콘솔(터미널, 명령프롬포트, cmd 다 같은말)에서 python으로 main.py을 실행한다고 해봅시다.

C:\project>python main.py
hello 모듈 시작
hello.py __name__: hello
hello 모듈 끝
main.py __name__: __main__

앞선, 스크립트파일 실행과 같이, hello.py 파일의 __name__ 변수의 'hello' 와 main.py 파일의 __name__ 변수의 '__main__' 이 들어가네요.

hello.py를 모듈로 가져왔을 때

하지만, 다음과 같이 python으로 이번에는, main.py가 아닌 !,  hello.py 파일을 실행해보면, 결과가 조금 다릅니다.

C:\project>python hello.py
hello 모듈 시작
hello.py __name__: __main__		// 이 부분
hello 모듈 끝

hello.py를 단독으로 실행했을 경우

이번에는, hello.py의 __name__ 의 변수값에 앞선 예시와 같은 'hello'가 아닌
모듈의 이름이 아니라( 즉, __name__ 이 아니라 ) __main__ 이 들어갑니다 ! 

즉, 어떤 스크립트파일이든, 파이썬 인터프리터가 최초로 실행한 스크립트 파일의 __name__ 변수에는 '__main__' 이 들어가는 것이죠. 이는 프로그램의 "시작점(entry point)"이라는 뜻입니다.

파이썬은 최초로 시작하는 스크립트 파일과 모듈의 차이가 없습니다.
어떤 스크립트 파일이든, 시작점이 될 수 있고, 모듈도 될 수 있는 것입니다.
그래서 __name__ 변수를 통해 현재 스크립트파일이 시작점인지 ? 모듈인지 ? 판단하는 것입니다.

계속 언급하고 있는,

if __name__ == '__main__' :

처럼 __name__ 변수의 값이 '__main__' 인지 확인하는 코드는 현재 스크립트파일이 프로그램의 시작점이 맞는지 판단하는 작업인 것입니다.

즉, 스크립트파일이 메인 프로그램으로 사용될 때와, 모듈로 사용될 때를 구분하기 위한 용도라 생각하면 됩니다.

 

📢 스크립트파일로 사용한 경우 vs 모듈로 사용한 경우

마지막으로, 그럼, python파일을 스크립트파일로 사용한 경우와 모듈로 사용한 경우의 예를 보고 마무리 합시다.

  • 스크립트파일로 실행한 경우

📝 calc.py

def add(a, b):
    return a + b
 
def mul(a, b):
    return a * b
 
if __name__ == '__main__':    # 프로그램의 시작점일 때만 아래 코드 실행
    print(add(10, 20))
    print(mul(10, 20))

📝 실행결과

// IDE에서 실행한 경우
30
200
====================================
// 명령프롬포트(cmd)에서 실행한 경우
C:\project>python calc.py
30
200

IDE에서 실행하거나, 명령프롬포트같은 파이썬 인터프리터로 실행하면 10,20의 합과 곱이 출력됩니다.

즉, "프로그램의 시작점" 일 떄는, if __name__ == '__main__' : 아래의 코드가 실행이 되는 것이죠.
( 이 예시에서는, print(add(10,20)) , print(mul(10,20)) 이 두 줄의 코드가 실행이 되는 것이죠. )

  • 모듈로 사용한 경우
>>> import calc
>>> 

모듈로 가져왔을 때는, 아무것도 출력이 되지는 않죠 ? 왜냐면 __name__ 변수의 값이 '__main__' 일 떄만, 10,20의 합과 곱을 출력하도록 만들었기 떄문입니다.

즉, 스크립트파일을 모듈로 사용할 때는, calc.add, calc.mul 처럼 "함수만 사용하는 것이 목적" 이므로, 10,20의 합과 곱을 출력하는 코드는 필요가 없습니다.

이럴때는, 위에처럼, 먼저 사용할 모듈을, import시킨다음, 다음과 같이 calc.add와 calc.mul 함수에 원하는 값을 넣어서 사용하면 됩니다.

>>> calc.add(50, 60)
110
>>> calc.mul(50, 60)
3000

 

📢 참고

  • 파이썬은 왜 프로그램의 시작점이 정해져 있지 않나요 ?
    ( C를 예로들면, int main(void) { } 같은 시작점이 있는데.. )

👉 파이썬이 처음에 개발 될 당시에는 리눅스(Linux/Unix)에서 사용하는 "스크립트언어" 기반이었기 떄문에, 프로그램의 시작점이 따로 정해져 있지 않았다고 합니다.
보통 리눅스/유닉스의 스크립트파일은 파일 한 개로 이루어진 경우가 많은데요.스크립트 파일 자체가 "하나의 프로그램" 이다 보니, 시작점이 따로 필요하지 않았다고 합니다.
하지만, 앞서말한 "C언어", "자바(Java)" 같은 프로그래밍언어는 처음 만들어질 때부터, 소스 파일을 여러 개 사용했기 때문에 여러 소스 파일의 함수들 중에서도 시작함수(main)을 따로 정해 놓은 것입니다.

 

📢 마무리

이렇게, "파이썬" = "스크립트언어" 이고, 이러한 스크립트언어의 특성 때문에, 전에 본인이 , C,Java 같은 언어로만 프로그래밍을 하고 있었다면, 오늘 정리한, 내용이 낯선 부분이 분명히 있을 것입니다.

아무 이유없이, 처음 파이썬을 프로그래밍언어로  사용해서, 잘 알고 있거나, 애초에 이런 스크립트언어의 특징을 이미 잘 알고 있던 사람이라면, 당연히 알고 있는 내용일 수 있지만, 한번 쯤은, 정리를 해야할 내용인 것 같습니다.

오늘 내용의 저의 생각을 정리하자면, 간단하게..

  • 파이썬 코드도 역시, 하나의 파이썬 파일 안에서도, "메소드" 나 "클래스" 등을, 작은 기능이여도 분리시키고,
    if __name__ == '__main__' : 을 통해서, 호출해서 사용하는 방식의 코드 구현을 지향하자.
  • 파이썬 프로그램도 역시, "스크립트파일을 실행하는 방식"과, 필요한 모듈의 기능만 가져와 쓰도록하는 "모듈을 import 해서 사용하는 방식" 이 있다는 것을 알고, 이 둘의 사용법의 차이를 알자.

이렇게 되는것 같습니다. 감사합니다. 🙂

728x90
반응형

📢 sort( )

파이썬으로 알고리즘문제를 풀다보면, 여러 조건으로 sort(정렬)해야하는 경우가 있습니다.

일반적으로, 파이썬에서 sort 하는 방법은

  • .sort( )
  • sorted( )

이렇게, 2가지 방식을 사용할 수 있습니다.

 

📢 정리

  • sorted(Iterator) 형태와 Iterator.sort( ) 방식은, 결과적으론 똑같이 정렬된다.
    따라서, 아래의 설명은 두 방식모두 동일한 방식으로 적용됩니다.
  • sorted( )의 key 인자로, 내가 커스텀할 비교함수를 보내주거나, lambda식을 적용해서 정렬기준을 정해주면 된다.
  • 비교함수는 비교할 아이템의 요소를 반환하면 됩니다.
  • 비교할 아이템의 요소가 복수 개일 경우, 튜플로 그 순서를 내보내주면 됩니다.
    • Ex) sorted( Iterator, key = lambda x : ( x[0], x[1] ) )
    • - 를 요소앞에 붙이면, 현재 정렬방식과 반대로 정렬하게 됩니다.
  • Iterator.sort(reverse=True) 를 설정하면, 내림차순으로 정렬을 합니다. (default는 reverse=Fasle, 즉, 오름차순 )
728x90
반응형

+ Recent posts