[프로젝트] 내일배움캠프 화면구현 미니프로젝트 결과물

Asher Park·2022년 12월 8일
2
post-thumbnail

주제

뉴스피드가 포험 되어있는 서비스

뉴스피드란?
내 게시물을 포함한 모든 게시물을 볼 수 있는 공간


프로젝트 제목 / 설명

  • 프로젝트 제목 : 오늘 뭐먹죠 ( '5'늘은 뭐먹'' )
  • 프로젝트 설명 : 먹는 것을 좋아하는 사람들의 정보공유를 위한 서비스.
  • S.A : https://swwcoding123.tistory.com/14

팀원 및 담당 기능

1. 문성원 (팀장)

2. 강성주

  • DB 스키마 구성
  • 회원가입 시 패스워드 암호화
  • 유저 로그인 Log 저장
  • favicon.ico 디자인

3. 이진석

  • 회원가입 기능 및 예외처리 ( 패턴매칭 )
  • 닉네임, 아이디 중복체크
  • 마이페이지 회원정보 수정
  • 내가 '좋아요' 누른 게시물 모아보기
  • 내가 쓴 글 모아보기

4. 이지영

  • 로그인 기능 및 세션유지
  • 아이디 찾기
  • 비밀번호 재설정

5. 박현민

  • AWS S3 이미지 업로드
  • AWS RDS 연결
  • WYSIWYG 에디터 ( Quill )
  • 게시글 작성, 수정, 삭제
  • 게시글 상세 페이지
  • 댓글 작성, 삭제
  • 게시글 Pagination 및 댓글 Pagination
  • 게시글 정렬

Stacks

1. Front

  • HTML
  • Javascript
  • CSS
  • Bootstrap

2. Back

  • Python (Flask)
  • MySQL

3. DevOps

  • AWS S3
  • AWS RDS

4. Tool

  • VScode
  • Git
  • GitHub

파일구조

📦What-should-i-eat-today
 ┣ 📂static
 ┃ ┣ 📂image
 ┃ ┃ ┗ 📜favicon_pizza.ico
 ┃ ┣ 📂js
 ┃ ┃ ┣ 📜edit.js
 ┃ ┃ ┣ 📜find_id.js
 ┃ ┃ ┣ 📜find_pw.js
 ┃ ┃ ┣ 📜log.js
 ┃ ┃ ┣ 📜login.js
 ┃ ┃ ┣ 📜mypage.js
 ┃ ┃ ┣ 📜my_like_post.js
 ┃ ┃ ┣ 📜my_post.js
 ┃ ┃ ┣ 📜post.js
 ┃ ┃ ┣ 📜postlist.js
 ┃ ┃ ┣ 📜register.js
 ┃ ┃ ┣ 📜update_pw.js
 ┃ ┃ ┗ 📜write.js
 ┃ ┗ 📂styles
 ┃ ┃ ┣ 📜common.css
 ┃ ┃ ┣ 📜edit.css
 ┃ ┃ ┣ 📜find_id.css
 ┃ ┃ ┣ 📜find_pw.css
 ┃ ┃ ┣ 📜header.css
 ┃ ┃ ┣ 📜log.css
 ┃ ┃ ┣ 📜login.css
 ┃ ┃ ┣ 📜mypage.css
 ┃ ┃ ┣ 📜my_like_post.css
 ┃ ┃ ┣ 📜my_post.css
 ┃ ┃ ┣ 📜post.css
 ┃ ┃ ┣ 📜postlist.css
 ┃ ┃ ┣ 📜register.css
 ┃ ┃ ┣ 📜reset.css
 ┃ ┃ ┣ 📜update_pw.css
 ┃ ┃ ┗ 📜write.css
 ┣ 📂templates
 ┃ ┣ 📂components
 ┃ ┃ ┣ 📜edit.html
 ┃ ┃ ┣ 📜find_id.html
 ┃ ┃ ┣ 📜find_pw.html
 ┃ ┃ ┣ 📜header.html
 ┃ ┃ ┣ 📜log.html
 ┃ ┃ ┣ 📜login.html
 ┃ ┃ ┣ 📜mypage.html
 ┃ ┃ ┣ 📜my_like_post.html
 ┃ ┃ ┣ 📜my_post.html
 ┃ ┃ ┣ 📜post.html
 ┃ ┃ ┣ 📜postlist.html
 ┃ ┃ ┣ 📜register.html
 ┃ ┃ ┣ 📜update_pw.html
 ┃ ┃ ┗ 📜write.html
 ┃ ┗ 📜index.html
 ┣ 📜app.py
 ┗ 📜config.py

와이어프레임




API

https://www.notion.so/544626a1335d4a9189573caea30ea4d0?v=e9650d6cb19e4230b7101a62245e4fd8


DB구조


프로젝트 진행

1. GitHub Repository

https://github.com/ParkAsher/what-should-i-eat-today

  • 한 팀원의 GitHub에 Repository를 생성하고, Collaborator로 팀원들을 초대.
  • 각자의 Branch를 생성 후, 작업
  • 각자의 Branch로 Push 후, Pull Request 요청
  • 프로젝트 매니징 팀원이 확인 후, Merge
  • 최대한 commit message 규칙을 지키기 위해 노력
    https://beomseok95.tistory.com/328

2. 기능 구현

기본적인 커뮤니티 서비스의 동작 순서에 맞게 기능 우선순위를 두고, 각자 담당한 기능을 구현 시작.

회원가입

  • 닉네임, 아이디 중복체크 구현
sql = "SELECT * FROM Users WHERE user_nickname = %s"
rows = app.database.execute(sql, userNickname)

sql = "SELECT user_id FROM Users WHERE user_id = %s"
rows = app.database.execute(sql, userId)
  • 각각의 입력 양식 설정 ( 패턴매칭 )
let nickname = userNickname.search(/[ㄱ-ㅎ|ㅏ-ㅣ]/g);    
let idKorean = userId.search(/[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/g);
let idSpace = userId.search(/[`~!@@#$%^&*|₩₩₩'₩";:₩/?]/gi);
let pwNumber = userPw.search(/[0-9]/g);
let pwEnglish = userPw.search(/[a-z]/gi);
let pwSpece = userPw.search(/[`~!@@#$%^&*|₩₩₩'₩";:₩/?]/gi);
let nameNumber = userName.search(/[0-9]/g);
let nameSpace = userName.search(/[`~!@@#$%^&*|₩₩₩'₩";:₩/?]/gi);
// RFC 5322 이메일 형식 강화형
let emailCheck = userEmail.search(/^[-0-9A-Za-z!#$%&'*+/=?^_`{|}~.]+@[-0-9A-Za-z!#$%&'*+/=?^_`{|}~]+[.]{1}[0-9A-Za-z]/i);
  • 회원가입 시 패스워드 암호화
hashed_pw = bcrypt.hashpw(userPw, bcrypt.gensalt(rounds=10))

로그인

  • 로그인 정보 세션 저장
user_data = []
for record in rows:
    temp = {
        "id": record[0],
        "user_id": record[1],
        "user_pw": record[2].encode('utf-8'),
        "user_name": record[3],
        "user_nickname": record[4],
        "user_email": record[5],
        "signup_at": record[6].strftime("%Y-%m-%d %H:%M:%S"),
    }
    user_data.append(temp)
session['user-info'] = user_data[0]

아이디 찾기, 비밀번호 재설정



메인화면

  • 게시글 리스트 최신순, 추천순 기준으로 정렬하여 출력
if sort == "recommend":
    # 추천순
    sql = """
            SELECT p.id, p.title, u.user_id, u.user_nickname, p.content, p.thumbnail, p.created_at, p.recommend
            FROM Posts as p
            LEFT JOIN Users as u
            ON p.author = u.id
            ORDER BY recommend DESC, created_at DESC
            LIMIT %s, 8
        """
    rows = app.database.execute(sql, (post_count))
else:
    # 최신순
    sql = """
        	SELECT p.id, p.title, u.user_id, u.user_nickname, p.content, p.thumbnail, p.created_at, p.recommend
            FROM Posts as p
            LEFT JOIN Users as u
            ON p.author = u.id
            ORDER BY created_at DESC
            LIMIT %s, 8
        """
    rows = app.database.execute(sql, (post_count))
  • 로그인 한 회원의 정보 및 로그를 출력
  • 게시글 Pagination
	# 댓글 갯수 카운트
    sql = """
            SELECT COUNT(*) FROM Comments
            WHERE c_post_id = %s
        """
    rows = app.database.execute(sql, post_id)

    for record in rows:
        comment_list_count = record[0]	
    
    if comment_list_count == 0:
        comment_page = 0
    elif comment_list_count % 5 == 0:
        comment_page = comment_list_count // 5
    else:
        comment_page = math.ceil(comment_list_count / 5)
        <div class="post-pagination-wrap">
            {% if post_page != 0 %} {% if sort == "recommend" %} {% for i in range(post_page) %}
            <button
                data-index="{{i+1}}"
                type="button"
                class="post-pagination"
                onclick="get_post_list('{{i+1}}' , 'recommend')"
            >
                {{i+1}}
            </button>
            {% endfor %} {% else %} {% for i in range(post_page) %}
            <button
                data-index="{{i+1}}"
                type="button"
                class="post-pagination"
                onclick="get_post_list('{{i+1}}' , 'newest')"
            >
                {{i+1}}
            </button>
            {% endfor %} {% endif %} {% endif %}
		</div>

게시글 상세 페이지

  • 게시글 삭제 기능 구현
  • 댓글 작성, 삭제 기능 구현
  • 댓글 페이지 네이션
  • '좋아요' 기능 구현
@app.route("/api/post-recommend", methods=['POST'])
def post_recommend():
    post_id = request.form['post_id']
    user_num = request.form['user_num']

    sql = """
            UPDATE Posts SET recommend = Posts.recommend + 1
            WHERE Posts.id = %s
        """

    app.database.execute(sql, post_id)

    sql2 = """
            INSERT INTO Recommends(r_user, p_id)
            VALUES(%s, %s)
        """
    app.database.execute(sql2, (user_num, post_id))

    return ({'msg': "추천하였습니다!"})


게시글 작성

  • Quill Editor 적용
function imageHandler() {
    const input = document.createElement("input");

    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.click();

    input.onchange = function () {
        const formData = new FormData();
        const file = $(this)[0].files[0];

        formData.append('file', file);

        $.ajax({
            type: "POST",
            url: "/api/file-upload",
            cache: false,
            contentType: false,
            processData: false,
            data: formData,
            success: function (response) {
                let img_url = response['img_url'];

                const range = quill.getSelection();
                quill.insertEmbed(range.index, 'image', img_url)
            }
        })
    }
}
// quill init function
function quillInit() {
    let options = {
        modules: {
            imageResize: {
                displaySize: true
            },
            toolbar: {
                container: [
                    [{ 'font': [] }, { 'size': [] }],
                    [{ 'header': 1 }, { 'header': 2 }],
                    ['bold', 'underline', 'strike', 'blockquote', 'code-block'],
                    [{ 'list': 'bullet' }, { 'indent': '-1' }, { 'indent': '+1' },],
                    [{ 'color': [] }, { 'background': [] }],
                    [{ align: '' }, { align: 'center' }, { align: 'right' }, { align: 'justify' }],
                    ['link', 'image'],
                ],
                handlers: {
                    image: imageHandler(),
                },
            }
        },
        theme: 'snow',
    };
    quill = new Quill('#editor', options);
    quill.getModule('toolbar').addHandler('image', function () {
        imageHandler();
    });
    quill.on('text-change', function () {
        document.getElementById("post-content").value = quill.root.innerHTML;
    })
}
  • AWS S3 이미지 업로드
###############
# file upload #
###############
@app.route('/api/file-upload', methods=['POST'])
def file_upload():
    file = request.files['file']

    filename = file.filename.split('.')[0]
    ext = file.filename.split('.')[-1]
    img_name = datetime.datetime.now().strftime(f"{filename}-%Y-%m-%d-%H-%M-%S.{ext}")

    # s3에 이미지파일 업로드
    s3_put_object(s3, 'what-should-i-eat-today', file, img_name)

    # 올라간 이미지의 url
    image_url = f'https://what-should-i-eat-today.s3.ap-northeast-2.amazonaws.com/{img_name}'

    return jsonify({'img_url': image_url})

##########################
# image insert to aws s3 #
##########################
def s3_put_object(s3, bucket, file, filename):
    try:
        s3.put_object(
            Body=file,
            Bucket=bucket,
            Key=f'{filename}',
            ContentType="image/*",
            ACL='public-read'
        )
    except Exception as e:
        print(e)
        return False
    return True

게시글 수정

마이페이지

  • 내 정보 수정
  • 내가 '좋아요' 누른 게시물 모아보기
  • 내가 쓴 글 모아보기

프로젝트 하면서 아쉬웠던 점

1. 이지영

  • '비밀번호 찾기' 기능 구현 중, 성공 시 출력되는 메시지에 링크를 걸어 다음 화면으로 넘어가고 싶었지만 실패해서 아쉬웠다.
  • 이미 선언한 변수의 변수명을 잘 확인하지 못한 점이 아쉬웠다.
  • 각종 HTML 태그의 사용법이 미숙하였다.

2. 이진석

  • 코드의 흐름을 이해하지 못하여, 어디서 에러가 발생했는지 대처하기 힘들었다.
  • 패턴매칭을 이용한 예외처리에서 비슷한 코드를 병합해보려 하다가 실패함.

3. 강성주

  • DB와 SQL에 대한 학습 수준이 충분하지 않았음.
  • 가장 기초적인 부분에서 어려움을 느껴 추후 기초를 더 튼튼히 다져야한다고 생각함.

4. 박현민

  • 추가적인 기능 구현 아이디어 부족으로 인해 화면 구성이 많이 비어보이는 듯한 느낌이 들었다.
  • JS 에서 session을 저장하는 것이 다른 기능들의 구현에 더 효율적일 것 같다는 생각을 했다.
  • 디자인을 더 이쁘게 하고싶었다.

5. 문성원

  • 건강 문제로 인해 프로젝트에 적극적인 참여가 불가능 했던 점이 아쉬웠다.
profile
배움에는 끝이없다

0개의 댓글