창작물

나만의 실습 사이트 만들기 (3편)

ilsancityboy 2024. 6. 20. 06:07

https://ilsancityboy.tistory.com/74

 

나만의 실습 사이트 만들기 (2.5편)

https://ilsancityboy.tistory.com/73#comment21545088 나만의 실습 사이트 만들기 (2편)지난글https://ilsancityboy.tistory.com/72 나만의 실습 사이트 만들기 (1편)매번 sql injection 이나 XSS 등등 공격 기법을 사용해보기

ilsancityboy.tistory.com

지난 글에서는 sql을 연동하고 배경을 영상으로 바꾸기까지 완성했다.

이번 글에서는 커뮤니티처럼 실제 게시판, 글쓰기 , 글 목록과 목록 나누기 기능을 구현 할 것이다.

 


 

1. 데이터베이스 모델 정의

제일 먼저 게시판에 글을 작성하면 그 글이 저장되거나 , 저장된 글을 불러와서 화면에 띄우주기 위해선 

데이터베이스를 먼저 정의해야한다. 내가작성한 2편의 글에서도 user의 아이디나 비밀번호 등등 정의를 먼저 해주었는데 게시글 또한 똑같다.

따라서 app.py에 해당 코드를 먼저 추가해주었다.

 

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(150), nullable=False)
    author = db.Column(db.String(150), nullable=False)
    content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

게시글의 고유 id, 제목 , 작성자, 내용, 작성일 등등을 정의해주었다.

그럼 users라는 db파일 안에는 아래처럼 정의가 되어 저장될 것이다.

테이블 구도

그리고 post 테이블 안에는 저장되는 형식이

id title author content creates_at
1 제목입니다 홍길동 아 오늘 아침 뭐먹지 2024-06-20 05:15:20:01
2 제목2 김기명 재밌따 2024-06-20 05:20:10:24

 

2. 게시판 목록 페이지 구현 - board.html

이제 게시판 페이지를 만들 것 이다.

작성자의 코드는 이렇다.

<!-- templates/login.html -->
{% extends 'layout.html' %}
{% block title %}게시판{% endblock %}


{% block content %}
	<div class="text_wrapper">
        <section>
            <h2>게시글 목록</h2>
		<hr>
            <ul>
                {% for post in posts %}
                <li>
		    <h3><a href="{{ url_for('view_post', post_id=post.id) }}">{{ post.title }}</a></h3>
                    <p>작성자: {{ post.author }} | 작성일: {{ post.created_at }}</p>
                </li>
                {% endfor %}
            </ul>



	<hr>

        </section>
		     <a href="{{ url_for('create') }}">
            	       <button class="custom_button">글 작성하기</button>
        	     </a>
	</div>
{% endblock %}

 

여기서 보고 넘어가야할 것은 

<h3><a href="{{ url_for('view_post', post_id=post.id) }}">{{ post.title }}</a></h3>

이 코드인데

먼저 view_post는 라우트 함수 이름을 나타내며 post._id=post.id는 해당 함수에 전달되는 파라미터이다.

즉, 이 제목을 누르게 되면 view_post라는 함수에 post_id 파라미터를 전달한다는 뜻이다.

 

그럼 이제 app.py에 view_post 함수를 정의해주자.

 

3. app.py 수정

@app.route('/view_post/<int:post_id>')
def view_post(post_id):
    post = Post.query.get_or_404(post_id)
    return render_template('view_post.html', post=post)

이 코드를 추가해준다.

view_post 함수가 post_id 파라미터를 전달받아 실행하게 되면

    post = Post.query.get_or_404(post_id) 를 하는데
이는 post_id에 해당하는 글을 get , 즉 가져오란 뜻이고 만약 없다면 404 오류를 띄게 만든다.

그후 가져오는 결과를 view_post.html에 렌더링한다.

 

4. 게시글 보기 페이지 구현 - view_post.html

그럼이제 게시글을 눌렀을 때 해당 게시글을 보여주는 html을 만들자.

<!-- templates/login.html -->
{% extends 'layout.html' %}
{% block title %}게시글{% endblock %}

{% block content %}
	<div class="text_wrapper">
            <article>
                <h2>{{ post.title }}</h2>
                <p>작성자: {{ post.username }} | 작성일: {{ post.created_at }}</p>
                <p>{{ post.content }}</p>
            </article>

	    <a href="{{ url_for('board') }}">
            	       <button class="custom_button">글 목록</button>
       	    </a>
	</div>
{% endblock %}

 

app.py에서 가져온 결과값 즉, 사용자가 누른 post_id에 맞는 데이터들을 가져오게되고 
그럼 view_post.html에선 해당 데이터들을 보여주게된다.

 

5. 페이지네이션 구현

이제 글 목록 페이지인 board.html에서 글 목록을 나누어 표시하게끔 구현할 것이다.

만약 100개의 글 데이터가있다면 페이지 내에서 100개의 글을 보여주기엔 무리가 있기에

우리가 알듯이 다음페이지와 이전페이지를 넘어다니며 글 목록들을 볼수있게 구현해보자.

from sqlalchemy import desc  # desc 함수를 임포트합니다
.
.
.
<!-- 기존 코드 -->
.
.

@app.route('/board')
def board():
    page = request.args.get('page', 1, type=int)
    per_page = 3
    posts = Post.query.order_by(Post.created_at.desc()).paginate(page=page, per_page=per_page, error_out=False)
    return render_template('board.html', posts=posts)

 

app.py 코드내의 board 페이지를 라우팅해주는 부분을 수정한 결과다.

page 라는 변수를 선언하는데 이때
request.args.get() 함수는 HTTP 요청에서 쿼리 매개변수를 가져오는 메서드이다.

매개변수의 내용은 page라는 매개변수를 가져오며 이때 page는 1이 기본값이고 타입은 정수로 선언한다.

per_page는 한 페이지에 보이기 될 게시글의 개수이다.

Post.query 는 쿼리의 post 테이블에서 시작한다는 뜻이며 order_by() 함수를 사용하여 정렬 기준을 설정한다.

Post.created_at.desc()는 created_at 필드를 기준으로 내림차순(descending order)으로 정렬한다.

즉, 최신 게시글이 먼저 나오게 된다.

paginate() 메서드는 페이지네이션을 적용한다는 뜻

page 매개변수는 현재 페이지 번호를 나타내며, 위에서 가져온 쿼리 매개변수 page의 값을 사용하고

per_page 는 위에서 선언한 3을 담고있는 변수인 per_page로 설정.

즉 3으로 설정한다.

error_out= 은 페이지 번호가 범위를 초과해도 오류를 나타내지않고 빈 페이지를 반환하게끔 false설정

board.html' 템플릿 파일을 렌더링하고, posts 변수를 전달한다.

 

            {% if posts.has_next %}
                <a href="{{ url_for('board', page=posts.next_num) }}">
            	       <button class="custom_button">다음 페이지</button>
        	</a>
            {% endif %}


            {% if posts.has_prev %}
                <a href="{{ url_for('board', page=posts.prev_num) }}">
            	       <button class="custom_button">이전 페이지</button>
        	</a>
            {% endif %}

posts.has_next는 다음 페이지가 있는지를 나타내는 불리언(boolean) 값이다.

이값이 true인경우 이전페이지가 있다는 뜻이고 false일시 없다는 뜻이다.

따라서 if 문으로 만약 다음 페이지가 있다면 <a> 즉, 다음페이지로 넘어가는 버튼을 보여주고 없다면 버튼을 보여주지 않는다.

다음 페이지 버튼이 있다고 가정하고 버튼을 누르게되면 url은 board로 이동시켜주며 page= 는 post의 다음 숫자를 매개변수로 넘겨준다.

즉 지금 내가 board/page=1 에 있다고 가정하고 버튼을 누르게되면 board/page=2 로 이동하게 되는것.

page의 다음 페이지가 false 즉, 다음 페이지가 없을때 까지 이동할 수 있게 된다.

 

 

6. 게시글 작성 페이지 구현 - create.html

<!-- templates/login.html -->
{% extends 'layout.html' %}
{% block title %}로그인{% endblock %}


{% with message = get_flashed_messages() %}
	{% if messages %}
		<script>
			alert("{{messages[-1]}}")
		</script>
	{% endif %}
{% endwith %}

{% block content %}

 

	<div class="text_wrapper">
	 <div class="form_group">
            <h2>게시글 작성</h2>
	    <hr>
            <form action="{{ url_for('create') }}" method="post">
                <label for="title">제목:</label>
                <input type="text" id="title" name="title" required>
                
                <label for="author">작성자:</label>
                <input type="text" id="author" name="author" required>
                
                <label for="content">내용:</label>
                <textarea id="content" name="content" rows="10" required></textarea>
                
                <button class="custom_button">게시글 작성</button>
            </form>
		
		     <a href="{{ url_for('board') }}">
            	       <button class="custom_button">글 목록</button>
        	     </a>
	 </div>
	</div>
{% endblock %}

 

이정도는 이제 쉽죠??

 

7. 게시글 작성 페이지 라우팅

@app.route('/create', methods=['GET', 'POST'])
def create():
    if request.method == 'POST':
        title = request.form['title']
        author = request.form['author']
        content = request.form['content']
        new_post = Post(title=title, author=author, content=content)
        try:
            db.session.add(new_post)
            db.session.commit()
            return redirect(url_for('board'))
        except:
            flash('게시글 작성 중 오류가 발생했습니다.', 'danger')
            return redirect(url_for('create'))
    return render_template('create.html')

 

app.py에 해당 코드를 추가. (게시글 작성시 데이터베이스에 저장하는 코드)

굳이 코드에서 봐야할 곳을 찾자면 

        new_post = Post(title=title, author=author, content=content)

인데 이는 받아온 데이터들 즉, title , author , content 데이터들을 new_post 라는 변수에 잠깐 담아서 

            db.session.add(new_post)

db에 넣어준다는 뜻이다.

 

 

 

 


결과물

 

 

다음 글에선 쿠키,세션 추가와 이를 활용해 작성자를 직접 입력하는 것이 아닌 작성자 id는 쿠키 세션으로 인해 고정되어 저장되게끔 구현해 보겠다.