프로젝트 기간 : 11/14(월) ~ 11/18(금)까지 총 5일 간 진행
소속 : 내일배움캠프 4기 node.js 트랙, A반 8조
조원: 김민수B(팀장), 김택환, 이재관, 정성욱, 표정훈
프로젝트 url : http://studynodejs.com/members
버킷 리스트
버킷리스트
studynodejs.com
-목차-
1. 프로젝트에 대한 감상
2. 다음 프로젝트 때에도 유지되어야 하는 부분
3. 개선점
4. 다음 프로젝트 때에 도전해보고자 하는 부분
5. 느낀 점
1. 프로젝트에 대한 감상.
대부분의 부트캠프 학생분들에게 해당되는 사항이겠습니다만, 이렇게 누군가와 팀을 이루어 코딩 프로젝트를 협업으로 해결해 나간다는 것은 모두에게 처음인 과제였을 것입니다.
웹개발 종합반을 수강하며 알게 된 웹 개발의 대략적인 흐름, 파이썬 flask 와 몽고DB 활용, 그리고 서버, 클라이언트 간의 통신을 담당하는 ajax 기능까지. 그러한 지식들을 살려서 어떻게 실무에서 역할을 분담하고, 각자의 결과물을 하나의 프로젝트로 합칠 수 있는가.
물론 실제 실무는 이보다 더 어렵고 복잡하며 그 과정이 길어지겠지만, 지금은 그러한 작업들을 '체험' 해보는 것 만으로도 분명 가치 있는
행동이었을 것입니다.
우리의 프로젝트는 이제 막 시작했을 뿐, 앞으로 내일배움캠프를 지내가는데에 있어서 더 큰, 더 어려운 프로젝트들과 맞딱드리게 될 것입니다. 그러한 미래를 대비하기 위해, 우리는 이번 '팀 소개 웹 개발 미니 프로젝트' 에서 어떠한 부분을 가져가야 하며, 어딜 고치고, 앞으로 어떤 코딩을 지향해야 하는지에 대해 이야기해 보겠습니다. 이를 KPT(keep, problem, try) 에 의거해서 말씀드려 보겠습니다.
2. 다음 프로젝트 때 에도 유지되어야 하는 부분, keep
이번 미니 프로젝트에서 다음 프로젝트로 가져가야 할 것들에 대해 말씀드려 보겠습니다.
1) 예상 외의 문제와 맞딱드렸을 때 해결하고자 하는 자세
2) 기존에 배운 것에 새로운 경험을 추가하고자 하는 의지
3) 배운 것 그 이상을 구현하고자 하는 노력
2-1) 문제를 해결하고자 하는 자세
다음은 'mongoDB 에 저장되는 각 레코드들에 고유 넘버인 id 값을 부여하고자 하는 노력들' 입니다.
우선 첫 번째로, '웹개발 종합반' 에서 배운 방법입니다.
해당 코드에서는 데이터베이스에 저장된 레코드의 총 갯수를 구해서, 이에 1을 더한 값을 새로운 레코드의 id 값으로 설정하고 있습니다.
허나 이러한 방법은 버그를 나을 가능성이 있습니다.
예를 들어, 위와 같이 연속적으로 방명록을 3개 입력하였다고 가정합니다.
시험 1, 시험 2, 시험 3은 순차적으로 id 값을 각각 1, 2, 3 이렇게 가지게 될 것입니다.
만약 이 상태에서 시험 2 를 삭제하고, 새롭게 '시험 4' 라는 방명록을 저장하게 된다면 어떻게 될까요?
이렇게 되면, 시험 3 과 시험 4는 똑같은 id 값을 가지게 됩니다.
시험 3이 작성된 시점에서는 이미 시험 1, 시험 2 라는 방명록이 있기에 새롭게 부여되는 Id 값이 '3' 이 됩니다.
허나 시험 2가 삭제된 상태에서, 남은 방명록은 시험 1, 시험 3 뿐입니다. 따라서 새롭게 작성된 시험 4는 id 값으로서 같은 '3' 을 가지게 됩니다.
실제로 시험 4를 삭제하니, 실제로 삭제된 것은 시험 3 이었습니다.
이는 시험 3과 시험 4가 둘 다 id 값은 같은데, 먼저 생성된 것이 시험 3 이기 때문 입니다.
웹개발 종합반에서 배운 방법이 잘못됐다, 라는 것이 아닙니다. 그 때와는 상황이 달라졌고, 따라서 해결 방법 또한 달라져야 합니다.
이에 대해서 몇 가지 해결책을 제시하였습니다.
첫 번째, 랜덤 값을 id에 저장하는 방법입니다.
# 개인 소개 페이지에서 방명록 입력 시 insert 하는 구문.
@app.route("/members/minsoo/post", methods=["POST"])
def minsoo_post():
name_receive = request.form['name_give']
comment_receive = request.form['comment_give']
count = random.uniform(1, 1000)
doc = {
'num': count,
'name': name_receive,
'comment': comment_receive
}
db.minsoo.insert_one(doc);
return jsonify({'msg': 'POST 연결 완료!'})
위 코드에서는 1 부터 1000 사이의 실수를 랜덤하게 만들어서 id 값으로서 활용하고 있습니다.
실재로는 333.458953489, 711.358945893 이런 식으로 값이 저장되기에 방명록의 갯수가 수 만개 이상 되지 않는 이상은
곂치지 않을 것 처럼 생각됩니다.
허나, 단 0.0000001% 라도 id 값이 곂치게 될 가능성이 존재합니다. 이는 '절대 고유' 라는 규칙에 위배됩니다.
두 번째, date 함수를 사용해서 밀리초 단위로 시간을 뽑아내 id 값으로 저장하는 방법입니다.
# 입력기능
@app.route("/members/lee/post", methods=["POST"])
def lee_post():
lee_receive = request.form['lee_give']
name_receive = request.form['name_give']
time_receive = time()
doc = {
'lee': lee_receive,
'name': name_receive,
'time' :time_receive
}
db.lee.insert_one(doc)
return jsonify({'msg': '등록 완료'})
처음 방명록을 DB에 저장할 때 현재 시간도 함께 저장합니다. 그리고 이를 id 값으로서 활용합니다.
만에 하나, 정말 정말 만에 하나 밀리초 단위로 동일한 시간에 다수의 방명록이 동시에 입력될 가능성이 있긴 합니다만,
랜덤 실수보다는 더 정확하지 않을까 합니다.
세 번째, DB에 레코드가 없을 경우 id 를 1로 설정, 기존에 레코드가 있으면 해당 레코드의 id 값에 +1 을 해서 새롭게 등록.
# home 에서 게시판 작성 시 DB에 insert
@app.route("/members/notice", methods=["POST"])
def home_insert():
title_receive = request.form['title_give']
writer_receive = request.form['writer_give']
content_receive = request.form['content_give']
date = datetime.datetime.now()
notice_id = 0
print(date)
notices_list = list(db.notice_board.find({}, {'_id': False}).sort('notice_id', -1))
if notices_list == []:
notice_id = 1
else:
notice_id = notices_list[0]["notice_id"] + 1
doc = {
'notice_id': notice_id,
'title': title_receive,
'writer': writer_receive,
'content': content_receive,
'date': date
}
db.notice_board.insert_one(doc);
return jsonify({'msg': '기록완료!'})
언뜻 보면 이 방법도 좋아 보이지만, 이럴 경우 문제가 하나 발생합니다.
예를 들어서 레코드가 100개가 저장되어 있는데, 100번째 레코드를 삭제해서 새롭게 레코드를 등록할 경우 그 레코드의 id 값은 100이 됩니다. 즉, 삭제 상관 없이 해당 DB에 여태껏 등록되어 왔돈 총 레코드의 누적 갯수를 산출해내지 못한다는 것입니다.
결국 몽고DB의 컬렉션에 레코드가 등록되면 기본적으로 값을 할당해주는 ObjectId 를 사용하는 수 밖에 없어 보입니다.
허나 한창 프로젝트를 개발하던 당시의 저희들은 ObjectId 를 활용하는 방법을 알지 못했고, 아쉽게도 차선책을 선택하게 되었습니다.
2-2) 기존에 배운 것에 새로운 경험을 추가하고자 하는 의지
다음 사진은, 방명록을 등록할 시 랜덤으로 이미지를 선정하여 함께 추가해주는 모습입니다.
해당 기능은 대략 다음과 같은 코드로 이루어져 있습니다.
-------------------------
const images = [

위는 '비밀번호' 를 활용한 방명록 기능이고, 아래는 페이징이라는 코드를 새롭게 추가한 게시판 입니다.
둘 다 기존에 웹개발 종합반에서 배웠던 기능들에 '그렇다면, 여기서 조금만 더 추가해서 응용하면 더 멋진 기능을 만들 수 있지 않을까?' 라는 발상에서 추가된 기능들입니다.
이러한 추가적인 시도들은 사실 '과연 내가 이 기능을 만들 수 있을까?' 라고 사전에 겁을 먹게 만들곤 합니다.
그리고 실제로 코드 구현을 시도한 끝에 '아... 이건 더 이상 만들지는 못하겠다' 라고 좌절하게 될 수도 있습니다.
하지만 저희 조원들은 다른 조들이, 그리고 저희 개개인들이 그러한 위험부담을 짊어지고서도 일단 시도를 하는 모습을 보며 용기를 얻을 수 있었습니다. 다른 이들 또한 알고 있는 수준은 저희와 크게 다르지 않는데, 소비자에게 어떠한 기능을 제공하고 싶다 라는 아이디어를 최선을 다해 구현화 시킬려고 노력하는 모습을 보면, 나도 저들처럼 해야겠구나 라며 용기를 얻을 수 있었습니다.
3. 개선점
3-1) ID 활용
앞서 2번 목차에서 설명해 드렸듯, 저희는 mongoDB 의 컬렉션이 갖는 고유한 넘버, ObjectId 를 활용하지 못했습니다.
이는 해당 필드에 저장되는 값이 일반적인 int 가 아닌, Object 타입의 자료였다는 것을 몰랐기 때문입니다.
사실 이 id 값을 활용할 수만 있다면, 필요 없는 코드들을 구현해서 임의로 id값을 설정하고자 시간과 노력을 소모할 필요도 없게 됩니다.
----
x = ObjectId()
ObjectId("507f1f77bcf86cd799439011")
----
----
const ObjectId = require("mongodb").ObjectId;
for (let i = 0; i < 3; i++) {
const newObjectId = new ObjectId();
let ctr = 0;
const id = newObjectId.toString();
.
.
.
}
----
만약 ObjectId() 라는 형태로 몽고DB의 고유 id 값을 다룰 수 있다는 사실을 알고 있었다면 우리는 보다 더 쾌적한 코딩 작업을 수행할 수 있었을 것입니다. 이러한 사실을 알아내지 못한 이유로는 무엇이 있을까요?
1. 몽고DB의 컬렉션을 좀 더 자세히 살펴봐야 했었다
2. 자료형에 대한 이해도가 부족했다.
3. 구글링이 부족했다.
아마 위에 3 항목 모두 다 해당하겠지요.... 다음 프로젝트 때에는 특히 더 자료형에 대한 이해도와 구글링 방법에 대한 숙련도를 좀 더 높여야 겠다는 생각이 들었습니다.
3-2) restful 한 api 구성. 정확히는, 주소의 이름의 통일성.
위는 저희 조가 서버-클라이언트 간 통신을 하는데에 있어서 api 기능들을 체계적으로 다루기 위해 설정한 이름들 입니다.
보시면 아시겠지만, 'main page 게시판 글 가져오기' 의 http verbs 는 post 인데, 정작 route 의 이름은
'/members/getNotice' 입니다. 참고로 이 이름은 팀장인 저 김민수가 지은 이름입니다......
이러한, 통일되지 않은 방식은 협업을 하는데에 있어서 직관적으로 코드를 이해하는데에 어려움을 줍니다.
이러한 잘못된 방식은 비단 route 뿐 아니라 변수명, 함수명에 있어서도 동일하게 적용됩니다.
협업에 있어서 체계적인 이름 정의가 얼마나 중요한지 프로젝트를 합치는 과정에서 충분히 배울 수 있었습니다.
다음에는 프로젝트 제작에 앞서서 이러한 기본적 '정책' 들을 정하고 들어가야겠구나 라고 느끼게 해주는 부분이었습니다.
4. 도전해보고 싶은 것들
우선 떠오르는 것들이 2가지가 있습니다. 하나는 대댓글, 다른 하나는 '템플릿 통합' 입니다.
4-1) 댓글, 대댓글
위의 사진은 게시판의 글을 클릭했을 시, 해당 게시글의 상세 내용을 모달창으로 보여주는 모습입니다.
보시면 아시겠지만, 댓글 기능이 없습니다. 이번 프로젝트 때에는 저희의 실력의 한계로 인해서 댓글 기능을 구현하지 못했습니다만, 다음에 시간적으로 기회가 된다면 댓글 기능을 구현하여 프로젝트에 반영해보고 싶습니다.
댓글을 구현하기 위해서는 어떠한 노력들이 필요할까요?
우선은, 작성된 댓글이 어떤 게시글의 댓글인지를 알아야 합니다.
위의 사진은 해당 게시글에 대한 DB 정보 입니다. 보시는 바와 같이 각각의 게시글들은 notice_id 라는 고유의 값을 가집니다.
댓글이 작성된다면, 이 notice_id 의 값을 같이 필드값으로서 가지게 될 것입니다. 그래야지 마치 join 으로 두 개 이상의 테이블을 연결하듯 해당 댓글이 어떠한 게시글에서 작성된 댓글인지를 알 수 있을 것입니다.
----
< insert 된 댓글 레코드>
_id_: ObjectId()
commemt_id: int
notice_id: "해당 댓글이 달린 게시글의 id"
.
.
.
----
추가로, 대댓글의 경우 해당 댓글이 몇 단계의 댓글인지에 대한 정보도 함께 입력해야 할 것 같습니다.
----
< insert 된 댓글 레코드>
_id_: ObjectId()
comment_id: int
level: int (해당 댓글이 comment_id 가 달린 댓글 중 몇 단계에 해당하는가)
notice_id: "해당 댓글이 달린 게시글의 id"
.
.
.
----
솔직히, 아직은 DB 구성이 잘 예상이 가지 않습니다. 허나 이러한 방식으로 계속 추구한다면, 기나긴 시행착오와 삽질 끝에 결국에는 완성되지 않을까 하는 기대감이 있긴 합니다.
4-2) 개인 소개 페이지의 템플릿 통합
예를 들어서, 메인 페이지에서 각 맴버의 카드를 클릭하여 route 로 새 html 을 랜더링 하는 단계에서, 해당 맴버가 어떤 맴버인지 구분할 수 있는 값을 route 에 함께 포함시킬 수 있다면 어떨까요?
# home 페이지에서 멤버 카드 클릭 시 랜더링되는 개인 소개 페이지.
@app.route('/member')
def member():
member_id = request.args.get("member_id")
print(member_id)
return render_template('member.html', member_id=member_id)
이렇게 할 경우, html 이 load 되지 않은 단계에서 해당 주소는 자신이 '누구의 개인 페이지' 인지를 인지할 수 있습니다.
function show_member () {
const memberId = document.getElementById('hiddenId').innerHTML;
$.ajax({
type: "POST",
url: "/member/getMember",
data: {member_id : memberId},
success: function (response) {
const member = response['result'];
let fulBody = `<div id="hiddenId">{{member_id}}</div>
<div id="wrap">
<div id="profile">
<div id="profileImg">
<img id="member_img" src="../static/img/${member["img"]}"/>
</div>
<div>
<p id="member_name">${member["name"]}</p>
</div>
<div>
<p id="member_intro">${member["intro_self"]}</p>
</div>
<div class="linkTage">
<a href="${member["blog"]}" target='_blank' id="member_blog">개인 블로그</a>
</div>
</div>
.
.
.
`
$("body").append(fulBody);
}
})
}
그렇다면, 페이지가 랜더링 됨과 동시에 해당 고유 값으로 get 요청을 보내서 데이터를 받아오고, 정해진 템플릿 안에 값을 뿌려주기만 한다면, 하나의 템플릿으로 여러 명의 개인 페이지를 나타낼 수 있을 것입니다.
다음 프로젝트 때에는 이처럼 보다 더 체계적으로, 그리고 효율적으로 페이지를 구성해보고 싶습니다.
5. 느낀 점
저희 A반 8조가 미니 프로젝트를 진행하며 느낀 것은, '서로가 서로에게 용기를 줄 수 있다' 는 것이었습니다.
개인적으로 저는 '비밀번호를 활용해서 정해진 사람만 방명록을 수정할 수 있도록 하자' 라는 생각은 전혀 하질 못했습니다.
아뇨, 어쩌면 했을 수도 있습니다. 단지 '내가 그런 기능을 구현할 수 있을까? 겁나는데...' 하면서 회피했을 지도 모르겠군요.
허나 협업을 함으로써, 자신의 생각과 계획을 여러 사람들과 공유함으로서 '이 팀원들이랑 함께라면 만들 수 있을지도 모르겠다' 라고 생각하게 되더군요.
그러한 긍정적인 영향력이, 협업이 가진 장점들 중 하나일지도 모르겠습니다.
'내일배움캠프_개발일지 > 내일배움캠프_미니프로젝트' 카테고리의 다른 글
미니 프로젝트 - 화면 구현 1 (1) | 2022.12.07 |
---|---|
내일배움캠프_미니프로젝트 5일차, 게시판을 만들어보자. (1) | 2022.11.18 |
팀 미니 프로젝트 "팀 소개 페이지 만들기" 결과물 정리. (0) | 2022.11.18 |
내일배움캠프_미니프로젝트_팀소개SA(Starting Assignments) 3일차 (0) | 2022.11.16 |
내일배움캠프_미니프로젝트_팀소개SA(Starting Assignments) 2일차 (0) | 2022.11.15 |