본문 바로가기
Tech Development/Python Backend (Flask API)

Python Backend - Study Notes 7

by JK from Korea 2022. 9. 3.

날짜: 2022.04.13

 

[6장: 데이터 베이스]

우리가 만든 미니터 API는 재실행될 때마다 모든 데이터가 없어진다. 데이터를 메모리상에 저장해서 터미널에서 API를 실행할 동안에만 데이터가 남아있기 때문에 사실상 데이터베이스라고 불릴만한 것이 없다. 그렇기 때문에 데이터를 영구적으로 보존하고 미니터 API와 DB (Data Base)를 연결하는 작업을 해 볼 것이다.

 

[Database System]

DB: 데이터를 저장 및 보존하는 시스템이다.

데이터베이스에 대해서 하나씩 알아보자. 먼저 데이터베이스는 크게 2가지 종류가 있다. 관계형 데이터베이스 시스템 (RDBMS, Relational Database Management System)과 NoSQL로 불리는 비관계형 (Non-relational) 데이터 베이스가 있다.

 

[관계형 데이터베이스 시스템]

관계형 데이터베이스 시스템은 데이터들이 서로 상호 관련성을 가진 형태로 표현한 데이터이다. 대표적으로 MySQL이 있다. 모든 데이터는 2차원 테이블로 되어 있어서 row과 column으로 구성되어 있다. 각 테이블은 고유 키 (primary key)가 있다. 밑의 테이블이 예시이다.

 

 

여기서 “tags”테이블의 “user_id”는 “users” 테이블을 외부 테이블과 연결시켜주는 외부 키 (foreign key)라고 한다.

 

[Normalization & Transaction]

Normalization (정규화)는 데이터 중복을 최소화 하도록 시스템을 구조화하는 작업이다. 잘못된 데이터가 저장될 가능성을 줄이며 디스크 공간을 효율적으로 사용할 수 있게끔 한다.

 

Transaction (트랜스액션)은 일련의 작업들이 마치 하나의 작업처럼 취급외서 모두 다 성공하거나, 모두 다 실패하도록 설계되어 있는 것을 말한다. 은행 시스템이 대표적인 예시이다. 은행 계좌 이체 시스템을 실행 중 돈을 보낸다고 가정하자. 돈을 보내는 작업을해서 본인 계좌 잔고는 줄었으나 받아야되는 사람 계좌에 입금이 되지 않았을 경우 시스템 실행 중 중간 과정에서 오류가 발생할 것이다. 이런 경우 시스템이 처음부터 끝까지 모두 제대로 실행되었을 때만 데이터베이스에 영구적으로 반영하고 중간에 오류나 실패가 일어나면 진행 절차를 없던걸로 하고 기존의 상태로 복구시키는 기능이 Database Transaction이다.

 

Relational Database는 Transaction 기능을 보장하기 위해서 Atomicity (원자성), Consistency (일관성), Isolation (고립성), Durability (지속성), 다 합해서 ACID 성질들을 가지고 있다.

 

ACID 제공 → Transaction  가능 → 일련의 작업들을 한번에 하나의 unit으로 실행하는 것

 

[SQL]

SQL: Structured Query Language

 

관계형 데이터 베이스에서 데이터를 읽거나 생성 및 수정하기 위해 사용하는 ‘언어’이다. Create (생성), Read (읽기), Update (수정), Delete (삭제), 모두 합해서 CRUD 기능을 제공하는 Relational DB 전용 언어이다.

 

SQL은 언어이기 때문에 고유의 문법도 존재한다. 필수적으로 SELECT, INSERT, UPDATE, DELETE, JOIN이 있다.

 

① SELECT: 이미 DB에 존재하는 데이터를 불러올 때 사용된다. “FROM”은 원하는 테이블 이름을 가르키고 “column”들은 읽고 싶은 column들을 직접 선택해주는 개념이다. 특정 row에 해당하는 값을 읽고 싶으면 불러오는 column 값 중에서 하나를 세부적으로 지정해주면 된다.

② INSERT: 데이터를 생성할 때 사용한다.

③ UPDATE: 기존 데이터를 수정할 때 사용한다. UPDATE를 할때는 주로 구체적인 (row, column) 데이터를 수정하기 때문에 WHERE 구문을 같이 사용한다.

④ DELETE: 데이터를 삭제할 때 사용한다.

⑤ JOIN: 여러 테이블을 연결할 때 사용한다.

 

[데이터베이스 설치하기]

MySQL은 오픈소스 관계형 데이터베이스로 homebrew를 사용해서 설치하면 된다. 지금까지의 학습일지에서는 replit 이라는 온라인 ide에서 API 개발을 진행해왔다. 하지만 데이터베이스 관련 여러가지 문제로 인하여 앞으로의 개발은 goormide라는 온라인 ide 플랫폼에서 진행할 것이다. goormide의 경우 개발환경을 구축하고 설정할 때 mysql 데이터베이스를 기본 옵션으로 제공해주기 때문에 지금까지의 과정에다가 데이터베이스 기능을 덧붙이는데 있어서 가장 좋은 옵션이었다. replit에서 데이터베이스를 구축하기 위해서 몇일간의 시도를 해보았지만 MySQL 데이터베이스를 지원하지 않기 때문에 불가능한 것으로 판단하고 ide를 갈아탔다. 개인 pc에서 현재 프로젝트를 진행한다면 터미널에서 brew install MySQL로 진행이 가능할 것이며 실제 MySQL 프로그램을 설치해서 운영하는 것까지 가능할 것이다. 하지만 나의 경우는 goormide로 진행하는 방법을 소개하도록 하겠다.

 

(대충 이런 느낌... https://dalconbox.tistory.com/37 사이트 참고해서 순차적으로 진행했다.)

 

 

(“root”은 메인 관리자 개념으로 현재 사용자가 root으로 불린다.)

 

위의 그림대로 명령어를 입력하면 MySQL 실행과 root 사용자만 접근할 수 있도록 비밀번호 설정을 마친 것이다. 위의 설명대로 만든 MySQL 데이터베이스 공간을 이제 API와 연결시키고 데이터베이스의 디테일을 채워나갈 것이다.

 

[API에 DB 연결하기]

이제 API에 MySQL 데이터베이스 시스템을 연결해서 데이터를 저장하기 시작할 것이다. 관계형 데이터베이스는 특성상 미리 테이블 구조와 관계를 구현해야하기 때문에 스키마 (schema)부터 설계해야 한다.

 

 

위의 그림에 있는 테이블들을 MySQL 데이터베이스에 실제로 생성해보도록 하자. goormide에서 MySQL을 실행시킬때 밑에 그림과 같이 실행해야한다.

 

 

MySQL DB에 접속한 것이다. “-u”는 user ID를 명시하는 옵션으로 그 옆에 “root”라는 사용자로 접속했음을 알려준다. “-p”는 비밀번호를 직접 입력하겠다는 것이다. 비밀번호를 입력해서 정상적으로 MySQL DB에 접속한 것이라고 판단하면 된다.

 

이제부터 터미널에 입력하는 모든 것들은 MySQL DB System에 실행된다. Python API Development, Client Terminal, Server Terminal이 아닌, MySQL DB System Terminal 환경이라는 것을 인지하자. 주의할 점은 우리가 DB System에 접속되어 있다는 것이다. 여기는 아직 비어있다. 우리가 데이터를 입력하지 않았기에 Empty Storage와 마찬가지이다. 이제부터 데이터 프레임을 설계하고 필요한 데이터를 입력하는 작업을 해보자. 먼저 미니터를 위한 데이터베이스를 생성하자.

 

 

이렇게 DB를 만들면 이제 “miniter” 라는 specific DB를 이용하겠다고 지정해줘야 된다.

 

[DB 사용하기]

miniter DB를 이용하기 위해서는 다음 명령어를 사용하면 된다.

 

 

이제 miniter DB에 돌아와서 꾸밀 수 있게된 것이다. 먼저 RDB는 테이블 구조로 이루어져있으므로 테이블부터 생성할 것이다. Miniter API를 위한 DB를 생성하려면 다음과 같이 MySQL 코드를 작성하면된다.

(users 테이블의 경우에는 만드는 과정은 생략했다. 그리고 MySQL 문법을 하나하나 입력하면서 배운것이 있는데, “;”이 자바에서와 같이 period의 의미가 있다. 즉 MySQL 명령어를 입력한 뒤에 semi-colon을 입력하지 않으면 명령어로 인식이 되지 않는다.)

 

 

(여기까지 해서 총 3개의 테이블을 완성했다.)

 

위의 MySQL 코드를 모두 입력해서 테이블을 완성했다. 하지만 아직 우리는 MySQL을 모른다. 그리고 MySQL 관련된 내용은 별도의 책에서 다룰 예정이다. 일단 API를 완성하기 위해서 이해해야되는 문법만 새로 익히고 넘어가자.

 

① NOT NULL : 해당 column 값이 null이 될 수 없다. 무조건 값이 존재해야한다.

② AUTO_INCREMENT : 해당 column 값이 자동으로 “1”씩 증가한다. 위에서 보면 “id”처럼 번호가 자동으로 갱신돼야 하는 곳에 사용된다.

③ DEFAULT CURRENT_TIMESTAMP : 해당 column 값이 생성된 시간을 기록한다. 따로 지정해주지 않으면 디폴트값으로 현재 시간이 자동 입력된다.

④ ON UPDATE CURRENT_TIMESTAMP :  해당 row 중에 값이 바뀌는 부분이 있으면 CURRENT_TIMESTAMP에 의해서 수정된 시간을 기록한다. row 별로 언제 업데이트가 되었는지 알 수 있어서 데이터가 바뀌는 것을 캐치하고 싶을 때 유용하다.

⑤ PRIMARY KEY : 특정 column을 고유 키 값으로 지정한다.

⑥ UNIQUE KEY : 해당 column에는 중복되는 값이 있으면 안된다. 주로 이메일 등록할 때 UNIQUE KEY를 지정해서 중복 등록을 시스템적으로 방지한다.

⑦ CONSTRAINT … FOREIGN KEY … REFERENCES : 외부 키를 걸 수 있다. 

 

여기까지해서 MySQL 문법을 대충 감 잡았으면 된다. 위의 테이블을 만듦으로써, 기존의 schema대로 DB가 생성된 것이다. 이제 DB를 API에 연결해서 API에서 DB를 조작할 수 있도록 해야한다.

 

[SQL Alchemy를 통한 API-DB 연결]

먼저 SQLAlchemy 부터 알아보자. SQLAlchemy는 API (python)와 DB (MySQL)를 연결할 수 있게 해주는 파이썬용 라이브러리이다. SQLAlchemy를 사용하면 파이썬 파일에서 DB를 연결할 수 있고, SQL 실행이 가능하다.

 

sqlalchemy 설치

 

SQLAlchemy는 라이브러리이기 때문에 파이썬 개발 환경에서 sqlalchemy의 기능들을 가져다가 사용하는 것이다. SQLAlchemy에서 사용하고자 하는 핵심 기능은 결국 MySQL 형태의 DB에 접근하는 것이다. 그러기 위해서는 MySQL용 DB API를 설치해야 한다.

 

what mysql connector python does

 

DB와 연결 가능한 언어를 파이썬에서 사용하기 해주는 SQLAlchemy와 실제 DB를 조작하는 기능들을 가지고 있는 DB API가 둘다 있어야지 파이썬과 MySQL이 의사소통을 할 수가 있는 것이다.

 

 

이제 SQLAlchemy를 사용해서 DB에 연결 요청을 보낼 것이다. 핵심 개념은 “파이썬으로 MySQL을 컨트롤 하는 것”이다.

 

 

① : “create_engine”은 DB에 연결하기 위해 사용된다. “text”는 SQL을 만들기 위해 사용된다.

③ ~ ⑨ : 변수 “db”는 우리가 이미 만들어 놓은 MySQL DB에 대한 정보이다. 파이썬과 데이터베이스를 연결하기 전에 SQLAlchemy을 거쳐가는 단계가 필요하다. 이때 SQLAlchemy가 DB에 정상적으로 접속할 수 있도록 제공해주는 정보이다.

⑪ : 위의 db 정보를 사용해서 “db_url”을 구성한다. db_url은 DB의 실제 위치이다.

⑫ : “create_engine”은 ①에서 sqlalchemy에 import한 클래스이다. sqlalchemy의 목적은 파이썬 개발환경에서 MySQL DB를 프로그래밍하기 위함이다. 즉, create_engine을 사용해서 DB에 접속, 수정 (프로그래밍)할 것이다. 주의할 점은 create_engine이 객체이기 때문에, create_engine 안에는 MySQL DB를 프로그래밍할 수 있는 다양한 메서드와 변수들이 있다. 그래서 이름 “db”를 create_engine 객체로 만들었다는 것의 의도는 앞으로 파이썬 API 환경에서 “db”를 사용해서 데이터베이스를 컨트롤하기 위함이다.

⑮ : “execute”은 create_engine 클래스의 메서드이다. 이제는 “db”를 통해서 접속/수정 하는 것이다. execute method는 SQL을 DB에 전송해서 실행, 그리고 정보를 읽고 (GET), 생성 (POST) 한다. SQL은 Structured Query Language라는 것을 잊지 말자. DB용 문법에 불과하기 때문에 일정한 명령어만 execute을 통해서 넘기는 것이다. execute method는 2개의 인자를 가지고 있는데, “SQL 구문”과 “SQL parameter value”이다. SQL parameter value는 dictionary 형태로 보내야된다. params에서 미리  정의해 놓은 부분이다. SQL 구문 부분은 SQL parameter value 부분에 있는 dictionary key-value pair를 읽어서 치환한다.

→ SELECT * FROM users WHERE name = :name

→ SELECT * FROM users WHERE name = “junho”

그런뒤 “.fetchall()”로  DB에서 GET 한다. 

⑰ : fetchall 메서드에 대해서 좀 더 설명을 해보자면 DB에서 데이터를 리스트 형태로 받는다. 각 row는 dictionary 처럼 사용할 수 있으며 (실제 dictionary는 아니다), column 대표 이름이 “key” 값으로 사용된다. 그래서 print 명령어에서 ‘name’, ‘email’을 key로 사용하는 것이다.

 

[DB 있는 miniter API 프로그래밍하기]

앞서 보았던 DB에 연결하는 파이썬 코드에서 “DB_URL”이라는 DB 정보를 담은 변수를 만들었다. DB 정보는 민감한 부분이다. 그래서 실제로는 따로 파일을 만들어서 저장해야한다. API와 같은 디렉터리에 “config.py” 이름으로 파이썬 파일을 만들고 다음 DB_URL 정보를 저장하자.

 

 

④ : 접속할 DB 주소이다. 우리는 컴퓨터에 설치되어 있는 DB를 접속하므로 주소가 localhost 이지만, 외부서버에 설치되어 있는 DB에 접속하려면 해당 서버 주소를 지정해줘야된다.

⑤ : 데이터베이스의 포트 넘버이다. RDB는 주로 3306 포트를 사용한다. API나 사이트와 마찬가지로 DB로 네트워크를 통해 연결되는 시스템이므로 포트정보가 필요하다.

⑥ : 실제 DB 이름이다.

 

DB 정보 파일을 관리함으로써 개인 접속 정보 노출을 방지하고 각 환경과 설정에 맞게 설정 파일을 적용할 수 있다.

 

기존의 app.py에서 config.py 부분을 파일로 불러올 것이다.

 

 

[중간 점검]

이전까지의 코드와 앞으로의 코드를 이해하기 위해 create_app 함수를 기준으로 스토리텔링을 하려고한다. 우리는 현재 vscode (나의 경우 goormide)라는 ide에서 miniter API를 프로그래밍 하고 있다. 우리가 만드는 API는 Flask라는 마이크로 웹 프레임워크를 기반이고, 그 이유는 백엔드 API를 만들기 위해서 필요한 기능들을 Flask가 미리 만들어 놓았기 때문이다. API의 이름은 app이며, app는 Flask 타입의 객체이다.

 

여기서 잊으면 안되는 것이 API의 역할이다. 우리가 무엇을 만들고 있는지는 알아야지. 아직까지 miniter API는 클라이언트의 HTTP Request를 받아서 명령대로 DB에서 필요로 하는 GET, POST 기능을 수행한다. 명령된 작업을 마치면 HTTP Response로 다시 클라이언트한테 돌려준다. HTTP Transmission 역할을 수행하기 위해서 서버 쪽은 DB의 데이터를 자유롭게 컨트롤 할 수 있어야한다. 그래서 우리는 ⑬과 같이 “create_app” 함수에 인자로 “test_config”를 넘겨주는 것이다. 만약 미리 지정해준 test_config 파일이 있으면 그 정보를 바탕으로 DB를 만들것이고, 그러지 않은 경우에는 DB에 대한 정보를 담고 있는 config.py를 DB 바탕 정보로 사용하는 것이다.

 

이제 sqlalchemy에서 가져온 create_engine 클래스를 통해서 DB와 연결을 진행할 것이다. 라인 ㉓에서 database 변수는 create_engine 객체가 되므로 sqlalchemy의 create_engine 클래스의 메서드들에 접근할 수 있다. 이전에 보았듯이 그 메서드 중 하나가 execute라는 것이다. 앞으로 database.execute을 많이 하게 될것이다. 이런 database 인스턴스를 app.database에 저장했다는 것은 이제 app.database는 sqlalchemy 모듈의 create_engine 클래스의 인스턴스라는 것이다. 즉, app을 통해서 이제 @route을 사용하면 엔드포인트를 생성하고, app.database를 하면 데이터베이스에 접속하는 것이다. 이렇게 엔드포인트와 데이터베이스를 모두 컨트롤 할 수 있는 app을 return 함으로서 함수가 완성된다.

 

Flask의 경우 create_app이라는 이름의 함수를 factory 함수로 인식한다.

 

Factory Applications Flask Documentation:  https://flask.palletsprojects.com/en/2.1.x/patterns/appfactories/ 

 

Factory 함수의 경우 Flask를 실행함과 동시에 호출이 된다. 데이터베이스와 연결된 전체 API 구조를 보면 더 잘 이해될 것이다. 

 

[다시 처음으로...]

우리는 이제 다시 API로 돌아갈 것이다. 처음에 만든 app을 데이터베이스와 부드럽게 연동할 수 있도록 엔드포인트를 수정하고 새로운 함수를 추가할 것이다.

 

728x90
반응형

댓글