DAO(Data Access Object) DB의 Data!!
DB에 연결되어서 앞단에서 데이터를 관리하는 게 DAO이다.
DAO에 findAll, findById, save와 같은 메서드를 만들어 두는 것이다.
그리고 데이터가 필요한 수많은 프론트들이 DAO에 요청하여 데이터를 받아간다.
메서드를 만들어두고 재사용이 가능하다는 장점이 있다.
스프링에서는 DAO를 만든적이 없고 Repository를 만들었다.
왜 DAO가 아니고 Repository일까?
DAO를 추상화시킨게 Repository인데 왜 추상화시켰는지 살펴보자.
옛날에 데이터가 필요할 때는 무조건 DB로 접근해야 했다.
지금은 꼭 DB에 가서 데이터를 가져올 필요 없이
API, 파일 등 데이터에 접근하는 방법이 다양하다.
원래 DB만 바라봤을 때는 DB 데이터에 접근하기 위해 DAO를 사용했는데
다른 데이터에 접근할 때는 DAO가 맞지 않다.
DAO의 Data가 DB Data를 말하기 때문이다.
API와 같이 외부에서 데이터를 제공해주는 것을 Provider라고 하는데
앞단에서 요청을 할 때 언제는 DAO에 요청하고, 언제는 Provider로 요청하려고 하면
앞단에 if 로직이 필요해진다.
하나만 요청했을 때 알아서 다 처리해줘야 하는데 이렇게 되면 위임되지 않는다.
그래서 DAO와 Provider를 추상화한 것을 Repository라고 한다.
결국 최전선에서 데이터에 접근하는 것을 Repository라고 하는 것이다.
스프링에서 repository의 내부는 DAO로 구현되어 있다고 개념적으로 볼 수 있다.
파이썬에서 지금 DB에 바로 접근할 것이기 때문에
repository를 만들 필요 없이 DAO를 만들어 실습해보자.
# Data Access Object DAO
from db import connect as db
def insert_one():
pass
def select_all():
pass
def select_one():
pass
def update_one():
pass
def delete_one():
pass
데이터를 받아야 insert를 할 텐데 dict 타입으로 바로 insert가 가능하다.
지난 시간에 insert 할 때는 배열로 순서대로 키 값을 넣어줬는데 변수로 바인딩이 가능하다.
원래 %s인데 %(username)s 로 사용하면 딕셔너리 키 바인딩이 가능하다.
다음과 같이 insert_one을 호출하면 알아서 파싱 하여 딕셔너리 데이터로 만들어준다.
insert_one(username="ssar", password="1234")
def insert_one(**data): # {"username":"ssar", "password":"1234"}
sql = "INSERT INTO my_member(username, password) VALUES(%(username)s, %(password)s)"
try: # 트랜잭션
db.cursor.execute(sql, data)
except Exception as e:
print(e)
db.conn.rollback()
return -1
db.conn.commit()
return 1
insert_one이 잘 실행되는지 확인하기 위해 새 파일에서 dao 파일을 import 해준다.
import member_dao as dao
# 한 건 insert
def set_data():
result = dao.insert_one(username="hong", password="1234")
print(f"result : {result}")
insert_one이 잘 실행되니까 dao에 다른 메서드도 완성시켜보자.
# Data Access Object DAO
from db import connect as db
# insert_one(username="ssar", password="1234")
def insert_one(**data): # {"username":"ssar", "password":"1234"}
sql = "INSERT INTO my_member(username, password) VALUES(%(username)s, %(password)s)"
try: # 트랜잭션
db.cursor.execute(sql, data)
except Exception as e:
print(e)
db.conn.rollback()
return -1
db.conn.commit()
return 1
def select_all():
sql = "SELECT * FROM my_member"
db.cursor.execute(sql)
rows = db.cursor.fetchall() # dict 컬렉션 타입
return rows
def select_one(**data):
sql = "SELECT * FROM my_member WHERE id = %(id)s"
db.cursor.execute(sql, data)
row = db.cursor.fetchone() # dict 타입
return row
def update_one(**data):
sql = "UPDATE my_member SET username=%(username)s, password=%(password)s WHERE id=%(id)s"
try: # 트랜잭션
db.cursor.execute(sql, data)
except Exception as e:
print(e)
db.conn.rollback()
return -1
db.conn.commit()
return 1
def delete_one(**data):
sql = "DELETE FROM my_member WHERE id=%(id)s"
try: # 트랜잭션
db.cursor.execute(sql, data)
except Exception as e:
print(e)
db.conn.rollback()
return -1
db.conn.commit()
return 1
import member_dao as dao
# 한 건 insert
def set_data():
result = dao.insert_one(username="hong", password="1234")
print(f"result : {result}")
# 전체 가져오기 select_all
def get_datas():
my_members_entity = dao.select_all()
print(my_members_entity)
# 한건 가져오기 select
def get_data():
my_member_entity = dao.select_one(id=1)
print(my_member_entity)
# 한건 업데이트하기
def update_data():
result = dao.update_one(id=1, username="kkk", password="8989")
print(f"result : {result}")
# 한건 삭제하기
def delete_data():
result = dao.delete_one(id=4)
print(f"result : {result}")
스프링에 @Transactional이 안 붙으면
커밋 롤백하며 트랜잭션 관리를 해주지 않는다.
뭔가 데이터를 변경할 때는 어노테이션이 붙어야 한다.
근데 SELECT 할 때는 DB의 데이터를 변경할 일이 없어서 커밋 롤백을 할 필요가 없기 때문에
어노테이션을 붙이지 않아도 된다.
영속성 컨텍스트는 영속화 되어있는 오브젝트에 변경된 게 있는지 지속적으로 관찰한다.
실제 데이터를 getter로 다 꺼내서 변경을 확인하는 게 아니라
기존에 있는 데이터를 복제하여 레퍼런스 주소만 비교하는 것이다.
계속해서 변경 감지를 하고 있으면 낭비가 심하다.
SELECT 하여 영속화되었을 때 변경 감지가 필요 없음에도
지속적으로 영속성 컨텍스트가 변경을 감지한다.
쓸데없이 변경 감지를 계속해서하며 더티 체킹을 하게 된다.
이때는 어노테이션을 붙여서 readonly 속성을 걸어준다
변경될 일 없다고 알려주는 것이다.
@Transaction(readonly=true)
커밋, 롤백을 하지 않는다.
왜 붙여 그럼?
영속성 컨텍스트에서 변경 감지 연산이 사라진다!!
스프링과 같이 컨트롤러를 만들어주는 경량 웹 프레임워크 flask
pip
pip는 gradle과 같은 패키지 관리자로 라이브러리를 관리해준다.
pip는 2점대 버전이고, pip3는 3점대 버전이다.
Python310 폴더까지 환경변수가 잡혀있는데
C:\Users\Administrator\AppData\Local\Programs\Python\Python310\Scripts 내부에
pip와 pip3 파일이 있다.
정확히 하려면 라이브러리를 설치할 때 pip3이라고 적어줘야 하는데
내부적으로 파이썬 3점대 버전을 사용하고 있으면 알아서 pip3으로 적용된다.
신경 쓸 필요 없음!!
파이썬의 환경변수는
C:\Users\Administrator\AppData\Local\Programs\Python\Python310 경로까지 등록되어있다.
그래서 Scripts 폴더까지 찾아가지 못한다.
이때 python -m을 붙여주면 스크립츠 폴더 내부까지 잡을 수 있다.
스크립츠 폴더 내부의 명령어를 사용할 수 있다!!
# C:\Users\Administrator\AppData\Local\Programs\Python\Python310\Scripts
# python -m pip install flask
from flask import Flask
flask = Flask(__name__) # __main__ 지금 내 파일을 실행하고있구나
flask.run()
__name__은 내가 현재 내 파일을 그대로 실행하고 있으면 __main__이 뜬다.
내가 내 파일의 주인이라는 것이다.
다른 파일에서 import 하여 실행했을 때는 그 파일의 파일명이 뜬다.
즉, __name__은 내가 내 파일을 사용 중인지 import 하여 사용하는지 알려주는 것이다.
5000번 포트로 도는 서버가 실행되었다.
포트번호 5000번은 디폴트 값이다.
이 서버에 설정값을 줘보자.
flask.run(
host="0.0.0.0", # anywhere
port=5000,
debug=True # 이 부분이 설정되면 파일 저장시 서버 자동 리로드 (개발 시 필요)
)
이제 아까 만들어둔 메서드들로 컨트롤러만 만들면 끝이다.
1. 스프링의 @PathVariable과 같이 주소에 변수를 바인딩하기 위해서는 <변수명>으로 꺽쇠를 사용한다.
2. flask 내부에 request를 import 하여 body 데이터를 받을 수 있다.
기본 파싱 기법이 x-www-form-urlencoded 타입이기 때문에
json으로 받기 위해서 .get_json으로 받아준다.
3. flask 내부에 jsonify를 import 하면
리턴시에 dict 타입 데이터를 json으로 변환하여 리턴해줄 수 있다.
jsonify로 dict 데이터를 감싸서 리턴해주자.
# C:\Users\Administrator\AppData\Local\Programs\Python\Python310\Scripts
# python -m pip install flask
from flask import Flask, request, jsonify
import member_dao as dao
flask = Flask(__name__) # __main__ 지금 내 파일을 실행하고있구나
# 컨트롤러
@flask.route("/my-member")
def list():
return jsonify(dao.select_all())
@flask.route("/my-member/<id>")
def detail(id):
return jsonify(dao.select_one(id=id)) # **data
@flask.route("/my-member/<id>", methods=['DELETE'])
def delete(id):
return jsonify(dao.delete_one(id=id))
@flask.route("/my-member/<id>", methods=['PUT'])
def update(id):
# data = request.data # x-www-form-urlencoded 파싱 기법
data = request.get_json # application/json 파싱 기법
# dict 타입이라서!
return jsonify(dao.update_one(id=id, username=data["username"], password=data["password"]))
@flask.route("/my-member", methods=['POST'])
def save():
data = request.get_json
# dict 타입이라서!
return jsonify(dao.insert_one(username=data["username"], password=data["password"]))
flask.run(
host="0.0.0.0", # anywhere
port=5000,
debug=True # 이 부분이 설정되면 파일 저장시 서버 자동 리로드 된다.(개발 시 필요)
)
이 파일 3개로 api 컨트롤러를 만들 수 있다!!
[출처]
https://cafe.naver.com/metacoding
메타코딩 : 네이버 카페
코린이들의 궁금증
cafe.naver.com
메타 코딩 유튜브
https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9
메타코딩
문의사항 : getinthere@naver.com 인스타그램 : https://www.instagram.com/meta4pm 깃헙 : https://github.com/codingspecialist 유료강좌 : https://www.easyupclass.com
www.youtube.com