TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 웹개발>

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(3)

by 기록자_Recordian 2024. 8. 8.
728x90
반응형
시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.

이전 내용(메모앱 1단계 - 3단계)
 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(1)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.메모앱 만들기 - 1단계: 애플리케이션 생성 플라스크

puppy-foot-it.tistory.com

이전 내용(메모앱 4단계)
 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(2)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용(메모앱 1단계 - 3단계) [파이썬] 플라스크(Fl

puppy-foot-it.tistory.com


5단계: 사용자별 메모 관리

 

메모 모델을 수정하여 각 메모가 특정 사용자에게 속하도록 설정하고 이를 바탕으로 사용자가 자신의 메모만 조회할 수 있도록 애플리케이션의 라우트를 개선

▶ 사용자별 메모 관리 구현 및 사용자 인터페이스 개선

 

◆ 사용자별 메모 관리 구현

- Memo 모델에 사용자 참조를 추가하여 각 메모가 어떤 사용자에게 속하는지 식별할 수 있게 한다.

# 사용자별 메모 관리 구현
class Memo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 사용자 참조 추가
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.String(1000), nullable=False)
    
    def __repr__(self):
        return f'<Memo {self.title}>'

이 변경으로 사용자가 자신의 메모를 관리하는 데 중요한 역할을 하게 된다. (Memo 인스턴스가 user_id 속성을 통해 연관된 User 인스턴스와 연결)

 

- 사용자별 메모 관리를 위한 메모 조회 라우트도 로그인한 사용자는 자신이 작성한 메모만 볼 수 있게 수정해 준다.

# 메모 조회
@app.route('/memos', methods=['GET'])
@login_required
def list_memos():
    memos = Memo.query.filter_by(user_id=current_user.id).all() #현재 로그인한 사용자의 메모만 조회
    return render_template('memos.html', memos=memos, username=current_user.username) #사용자별 메모를 표시하는 템플릿 렌더링

▶ 이 라우트는 로그인한 사용자의 ID(current_user.id)를 사용하여 Memo 테이블에서 해당 사용자의 메모만 필터링하고,

이 메모들을 memos.html 템플릿에 전달하여 사용자가 자신의 메모를 볼 수 있도록 한다.

 

- 메모 생성 기능에서 현재 로그인한 사용자의 ID를 Memo 모델에 저장하여, 해당 메모가 어떤 사용자에 의해 생성되었는지 식별할 수 있도록 한다.

# 메모 생성
@app.route('/memos/create', methods=['POST'])
@login_required
def create_memo():
    title = request.json['title']
    content = request.json['content']
    new_memo = Memo(user_id=current_user.id, title=title, content=content) #현재 로그인한 사용자 id 추가
    db.session.add(new_memo)
    db.session.commit()
    return jsonify({'message': 'Memo Created'}), 201

 

- 메모 업데이트 기능에서는 먼저 해당 메모가 현재 로그인한 사용자의 것인지 확인한 다음 메모를 업데이트할 수 있도록 설정한다.

# 메모 업데이트
@app.route('/memos/update/<int:id>', methods=['PUT'])
@login_required
def update_memo(id):
    memo = Memo.query.filter_by(id=id, user_id=current_user.id).first() #현재 사용자의 메모만 선택
    if memo:
        memo.title = request.json['title']
        memo.content = request.json['content']
        db.session.commit()
        return jsonify({'message': 'Memo updated'}), 200
    else:
        abort(404, description="Memo not found or not authorized")

 

- 메모 삭제 기능에서는 현재 로그인한 사용자가 해당 메모의 소유자인지 확인한 후에 삭제를 진행하도록 설정한다.

# 메모 삭제
@app.route('/memos/delete/<int:id>', methods=['DELETE'])
@login_required
def delete_memo(id):
    memo = Memo.query.filter_by(id=id, user_id=current_user.id).first() #현재 사용자의 메모만 선택
    if memo:
        db.session.delete(memo)
        db.session.commit()
        return jsonify({'message': 'Memo deleted'}), 200
    else:
        abort(404, description="Memo not found or not authorized")

◆ memos.html 작성

사용자별 메모 조회 라우트에서 표시해줄 memos.html 템플릿을 작성한다.

해당 템플릿은 HTML과 함께 jinja2 템플릿 엔진을 사용하며, memos.html 파일은 플라스크 애플리케이션의 템플릿 파일로, templates 폴더 내에 위치하여야 한다.

 

- memos.html: 사용자별 메모를 나열하는 템플릿

- app.py: 애플리케이션 초기화 및 라우팅 설정

▶ 플라스크 애플리케이션에서 render_template('memos.html', memos=memos) 함수를 사용하여 memos.html 템플릿을 랜더링할 수 있으며, 이때 memos 변수에 사용자별 메모 데이터를 전달할 수 있다.

 

이 템플릿은 플라스크 애플리케이션에서 사용자별로 메모를 나열하고, 사용자가 메모를 보고 추가, 수정, 삭제할 수 있는 인터페이스를 제공한다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>나의 메모</title>
    <style>
        /* 여기에 CCS 스타일을 추가할 수 있다*/
        body { font-family: Arial, sans-serif; }
        .memo { margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; }
        .memo-title { font-weight: bold; }
    </style>
</head>
<body>
    <h1>나의 메모</h1>
    <p><a href="/memos/create">새 메모 추가</a></p>
    {% for memo in memos %}
    <div clss="memo">
        <h2 class="memo-title">{{ memo.title }}</h2>
        <p>{{ memo.content }}</p>
        <a href="/memos/update/{{ memo. id }}">수정</a> |
        <a href="/memos/delete/{{ memo. id }}">삭제</a>
    </div>
    {% endfor %}
</body>
</html>

 

[memos.html 템플릿의 주요 기능]

  • 메모 목록: {% for memo in memos %} 루프를 사용하여 사용자의 모든 메모를 나열
  • 메모 제목 및 내용: 각 메모의 제목 ({{ memo.title }})과 내용 ({{ memo.content }}) 표시
  • 메모 수정 및 삭제 링크: 각 메모에 대한 수정 및 삭제 링크 제공. 이 링크들은 각각 /memos/update{{ memo.id }} 및 / memos/delete{{ memo.id }}로 연결
  • 새 메모 추가 링크: 사용자가 새 메모를 추가할 수 있는 링크 제공

▶ 플라스크 애플리케이션의 list_memos 뷰 함수에서 memos 변수를 전달받아 사용자별로 메모를 표시하는 데 사용되며, CSS 스타일은 추가적인 디자인 요소를 위해 사용되며, 필요에 따라 수정 가능


◆ 메모 관리 기능 테스트

(아직 프런트엔드 페이지가 구축되지 않았으므로) curl 명령으로 테스트를 진행한다.

▶ 실무에서도 백엔드는 curl 명령으로 API를 검증하는 것이 일반적

 

- 회원가입

curl -X POST http://127.0.0.1:5000/signup -d "username=새사용자1&email=새사용자1@example.com&password=새비밀번호"

 

- 로그인

curl -i -X POST http://127.0.0.1:5000/login -d "username=새사용자1&password=새비밀번호"

 

- 메모 생성

curl -X POST http://127.0.0.1:5000/memos/create -b "쿠키값" -H "Content-Type: application/json" -d "{\"title\":\"새 메모\",\"content\":\"메모 내용\"}"

★ 쿠키값은 Set-Cookie 헤더에 포함된 다음 값이 쿠키값이다.

상단의 로그인 테스트에서 나오는 내용에서의 쿠키값은

session=.eJwlzjEOwyAMBdC7MHcAYxucy0QfbNSuSTNVvXsj9QBPep-0ryPOZ9rexxWPtL88balbX0Yds1bwqqtNZXYe0GkBclqoMGqliIs37xRSGxig4ROIaIHcyXtmKS7gkd2mI-bUsnJTKUZ2i9xMh7h3UZWsjFotj3RHrjOO_4bT9wcbOC_L.ZrTLOA.D5wXSHGzuhGYjiKFW72uqxKXNqI

이며, 이를 위의 메모 생성 curl 테스트 코드에 대입해보면,

curl -X POST http://127.0.0.1:5000/memos/create -b "session=.eJwlzjEOwyAMBdC7MHcAYxucy0QfbNSuSTNVvXsj9QBPep-0ryPOZ9rexxWPtL88balbX0Yds1bwqqtNZXYe0GkBclqoMGqliIs37xRSGxig4ROIaIHcyXtmKS7gkd2mI-bUsnJTKUZ2i9xMh7h3UZWsjFotj3RHrjOO_4bT9wcbOC_L.ZrTLOA.D5wXSHGzuhGYjiKFW72uqxKXNqI" -H "Content-Type: application/json" -d "{\"title\":\"새 메모\",\"content\":\"메모 내용\"}"

그런데... 계속 에러가 발생해서 여기저기 찾아보다가, 

"서버 로그: ​​더 많은 정보를 제공할 수 있는 특정 오류 메시지가 있는지 서버 로그를 확인하세요" 라는 챗gpt의 도움을 받아, VS CODE의 터미널을 확인해 보니, 아래와 같은 에러 메시지가 나와 있었다.

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'user_id' in 'field list'")
[SQL: INSERT INTO memo (user_id, title, content) VALUES (%(user_id)s, %(title)s, %(content)s)]
[parameters: {'user_id': 4, 'title': '새 메모', 'content': '메모 내용'}]
(Background on this error at: https://sqlalche.me/e/20/e3q8)
127.0.0.1 - - [08/Aug/2024 23:08:10] "POST /memos/create HTTP/1.1" 500 -

다시 챗GPT에 검색해서 에러 발생의 이유를 물어보니,

열이 생성되지 않음: memo 테이블에 user_id 열이 생성되지 않았을 수 있습니다. 이는 테이블이 제대로 마이그레이션되지 않았거나 마이그레이션 스크립트에 열이 포함되지 않은 경우 발생할 수 있습니다.

모델과 데이터베이스 간의 불일치: SQLAlchemy 모델에는 'user_id' 필드가 포함될 수 있지만 해당 데이터베이스 테이블에는 포함되지 않습니다. 이러한 불일치는 데이터베이스 스키마가 ORM 모델과 동기화되지 않을 때 자주 발생합니다.

 

라고 나왔고, 해결방안으로 다음의 답변을 줬다.

1. 데이터베이스 스키마 확인: memo 테이블에 user_id 열이 있는지 확인하세요. 데이터베이스를 직접 쿼리하여 이를 수행할 수 있습니다.

DESCRIBE memo;

열이 누락된 경우 다음 단계를 진행하세요.

2.수동 마이그레이션: 마이그레이션이 작동하지 않으면 데이터베이스에 열을 수동으로 추가해야 할 수도 있습니다.
ALTER TABLE memo ADD COLUMN user_id INT;

 

그래서 MySQL을 켜서 확인해보니, 실제로 memo 테이블에는 'user_id' 라는 컬럼이 생성되어 있지 않았고,

ALTER TABLE memo ADD COLUMN user_id INT;

명령을 실행하여 user_id 컬럼을 추가한 후에, 위의 curl 명령어를 다시 입력해 보았다.

그리고 그 결과,

메모가 잘 생성되었다.

 

 

- 메모 조회

curl -X GET http://127.0.0.1:5000/memos -b "쿠키값"

본래의 memos.html 에서 jinja2 템플릿 엔진 관련 문법을 기반으로 기존에 동일 사용자 ID로 입력한 새 메모 추가, 메모 내용이 포함되어 있음을 확인할 수 있다.

 

- 메모 업데이트

아직 특정 메모 ID를 확인할 수 있는 방법이 없으므로, 메모의 ID는 메모 조회 결과에서 보이는 <a href='/memos/update/메모ID" 코드를 확인하여 메모 ID를 확인한다

curl -X PUT http://127.0.0.1:5000/memos/update/메모ID -b "쿠키값" -H "Content-Type: application/json" -d "{\"title\":\"업데이트된 제목\", \"content\":\"업데이트된 내용\"|"

 

 

- 메모 삭제

메모 업데이트와 동일하게 삭제할 메모 ID를 MySQL에서 확인한 후, 해당 ID로 메모 ID를 수정하여 명령한다.

curl -X DELETE http://127.0.0.1:5000/memos/delete/메모ID -b "쿠키값"

 

- 로그아웃

curl -X GET http://127.0.0.1:5000/logout -b "쿠키값"


다음 내용

 

[파이썬] 플라스크(Flask) - 플라스크 프로젝트 - 메모앱(4)

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용(메모앱 1단계 - 3단계) [파이썬] 플라스크(Fl

puppy-foot-it.tistory.com

 

 

 

728x90
반응형