※이 프로젝트는 사이트의 디자인만을 차용하고,
모든 코드는 COPY한 것이 아니라 설계부터 자체적으로 하였음을 명시합니다.
프로젝트 기간
2022.10.31~2022.11.10 (2weeks)
그라폴리오 사이트를 모델링 사이트로 선정한 이유
우리 팀은 세션 때 배웠던 이론을 바탕으로 심화, 응용하여 개발자로서 성장할 수 있는 사이트를 고르는 것을 최우선의 목적으로 두었다. 따라서 해보았던 기능들을 다시 적용해볼 수 있으면서도 새로운 기능을 학습할 수 있는 그라폴리오 사이트를 선정하게 되었다.
사이트의 성격
사진 공유 플랫폼으로, 누구든 사진작가가 되어 자신의 작품을 업로드하여 소통할 수 있다.
팀원소개
Front-end
한혜선, 김정현, 오다원
Back-end
김지수, 박수호, 송인찬
기술 stack
Front-end : React, Sass
Back-end : Node Express, Mysql, AWS
시연 영상
협업방식
Notion에서 칸반보드와 타임라인을 이용하여 티켓으로 일정 관리
Daily stand up meeting(PM 1:30) : 어제 작업한 사항, 오늘 해야할 사항 공유
Slack : 공지사항 전달
vscode liveshare : 실시간 코드 공유 및 피드백
git/github, zoom, google meet : 실시간 코드 리뷰, 함께 에러 해결
PR/merge meeting(PM 11:00 / Front-end) : 함께 merge, pull을 받고 충돌사항 해결
내가 담당한 부분
채널 페이지, 계정정보 페이지, 작품 상세 페이지 중 메인 컴포넌트
채널 페이지


채널 컴포넌트를 생성한 후, 고민이 생겼다.
내 채널과 작가채널을 어떻게 구분하게 할 것인가?
내 채널과 작가 채널은 같은 컴포넌트를 사용하기 때문에 컴포넌트 안에서 조건부 렌더링을 이용해야한다는 것 까지는 생각을 해냈다. 그런데 어떠한 조건으로 이것을 분리시켜야 하는것인가?
이것을 알기 위해서는 나(로그인 한 사람)와 다른 사람을 구분할 수 있어야했다.
이 사이트에서의 모든 유저는 user_id값을 갖게 되는데..
그럼 로그인 할 때 그사람의 user_id를 localStorage에 저장하고, 그 아이디와 같은 사람이 바로 내가 되겠구나!
이 방법을 사용하여 테스트를 해보았더니 내가 원하는대로 조건부렌더링이 잘 되었다.
코드는 아래와 같다.
<div className="channel-account-info-btn-wrapper">
{localStorage.getItem('id') == userInfo.user_id ? (
<button className="channel-account-info-me-btn">
<Link to="/accountInfo" style={{ color: '#00d084' }}>
계정정보 수정
</Link>
</button>
) : (
<div className="followBtns" onClick={handleToggle}>
{isLogin ? (
isFollow == 1 ? (
<div
className={isClick ? 'followBtn' : 'followingBtn'}
onClick={sendResult}
>
{isClick ? '팔로우' : '팔로잉'}
</div>
) : (
<div
className={isClick ? 'followingBtn' : 'followBtn'}
onClick={sendResult}
>
{isClick ? '팔로잉' : '팔로우'}
</div>
)
) : (
<div className="followBtn" onClick={clickLoginBtn}>
팔로우
</div>
)}
</div>
)}
계정정보 페이지

import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import './AccountInfo.scss';
import DeleteModal from './DeleteModal/DeleteModal';
const AccountInfo = () => {
const navigate = useNavigate();
const [modalOpen, setModalOpen] = useState(false);
const [accountInfo, setAccountInfo] = useState({
login_id: '',
kor_name: '',
eng_name: '',
email: '',
nickname: '',
profile_image: '',
});
//accountInfo에 변화가 생겼을 때(수정)
const onChange = e => {
const { name, value } = e.target;
setAccountInfo({
...accountInfo,
[name]: value,
});
};
//계정정보 fetch
useEffect(() => {
fetch('http://localhost:8000/user/accountInfo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token: localStorage.getItem('token'),
},
})
.then(res => res.json())
.then(result => setAccountInfo(result.data));
}, []);
// 삭제 모달창 노출
const showModal = e => {
e.preventDefault();
setModalOpen(true);
};
//수정된 계정정보 서버로 저장
const saveAccountInfo = e => {
e.preventDefault();
fetch('http://localhost:8000/user/accountInfo', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
token: localStorage.getItem('token'),
},
body: JSON.stringify(accountInfo),
}).then(res => res.json());
};
간단하지만 미처 생각지 못했던 방법
input 값이 수정될 때마다 현재의 값을 바로바로 어떻게 가져올지에 대해 고민을 하였는데, 구글링을 통해 onChange함수를 위와같이 작성하고, 각 요소에 name을 주어, name과 value를 가져올 수 있다는 것을 알게되었다. 해당 요소에 접근하는 방법이 useState나 useRef만 있는줄 알았는데, name을 이용할 수도 있구나.. 정말 간단한 방법인데 미처 생각하지 못하여 놀라웠다. 앞으로는 알고있는 지식 내에서도 응용할 수 있는 방법이 없나 생각을 많이 해봐야겠다.

//데이터 삭제
const deleteAccount = e => {
e.preventDefault();
fetch('http://localhost:8000/user/accountInfo', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
token: localStorage.getItem('token'),
},
})
//토큰값 삭제
.then(localStorage.removeItem('token'))
.then(localStorage.removeItem('profile_image'))
.then(localStorage.removeItem('id'))
.then(localStorage.removeItem('kor_name'));
if (!localStorage.getItem('token')) {
alert('채널이 삭제되었습니다.');
navigate('/works');
}
};
DB에서만 삭제하면 되는게 아니였어?
처음에는 채널을 삭제 버튼을 누르면 단순히 DB에서만 유저의 정보를 삭제하면 될 거라 생각했다.
그런데 잠깐, 채널을 삭제한 후에도 그 사람의 정보가 남아있다면 댓글작성이나 팔로잉이 가능하잖아? 이것을 어떻게 막을 수 있을까?
방법은 간단했다. localStorage에 남아있는 정보를 모두 삭제하면 컴퓨터는 현재 사용중인 유저가 없다고 생각하게된다.
따라서 채널 삭제 버튼을 누르면, DB에서 유저의 정보가 삭제됨과 동시에 localStorage에 담겨있는 token, id, 한글이름, 프로필이미지 등 모든 유저의 정보가 삭제되도록 하였다.
질문은 나의 원동력
특정 상황에서 계정정보가 수정되지 않는 에러에 직면했던 적이 있다. 팀원한테도 물어보고 구글링도 해보고 혼자 끙끙앓았지만, 해결하기 어려웠다.
그렇다면.. '커뮤니티 뒀다 뭐해? 커뮤니티를 이용하자!' 라고 생각했던 나는, wecomunity에 발생한 에러와 시도했던 방법, 내가 생각하는 문제점 등을 상세하게 적어 올렸다. 그 결과 mentor_youjin 님으로부터 해결방안을 얻어냈다.
해결방안은 옵셔널 체이닝을 이용하여 accountInfo가 있을 때만 정보를 띄워주는 것이었다.
이 과정을 통해 내 문제상황을 파악하는 방법과 질문을 작성하는 방법에 대해 배울 수 있었다.
+ 어느날, 우리 팀원 중 한 분이 나에게 메일을 보내주셨는데, 인기질문 top 5 중 내 질문이 3개나 들어있다고 하셨다. 뿌듯함이 밀려왔다.


작품 상세정보 페이지 중 메인 컴포넌트

params를 이용하여 작품의 id를 back-end로 넘겨, 데이터를 받아왔고, useRef를 이용하여 댓글의 value에 접근하여 현재 댓글의 데이터를 받아와 저장시켰다. 댓글과 태그는 컴포넌트화하여 재사용하였다.
Front-end? Back-end?
댓글 기능을 만들 때, front-end에서 먼저 댓글이 달리게 하는 로직을 짜고 back-end로 넘겨줘야하는건지 아니면 바로 back-end로 새로운 댓글 데이터를 넘기고 댓글 배열에 담아서 다시 response로 돌아오는 값을 받아야할지 헷갈렸는데 back-end 팀원과 의견을 조율하여 후자의 방식을 선택하였다.
하나의 기능을 구현하는 데에 있어서도 다양한 방법이 존재하기 때문에 front와 back간에 의견조율이 굉장히 중요하다는 것을 알 수 있었다.
짧다면 짧고, 길다면 긴 2주동안,
이론적 한계
저번달에 세션을 들을 때만 해도 나는 내가 이론을 거의 완벽히 이해했다고 생각했고, 바로 구현할 수 있을거라 생각했다. 하지만 실제로 프로젝트에 들어서니 내 생각은 틀렸다는 것을 알게되었다.
배우지 않았던 기능을 구현해야하는 것들이 많았고, 또 배운 시간이 짧다보니 아직 익숙해지지 않아 구글링을 하고 또 팀원들에게 물어보면서 기능들을 이해하고, 익혀야했다.
소통과 문서화는 중요하다
'개발자에게는 소통능력과 문서화능력이 중요하다.' 라는 말은 많이 들었지만, 이유를 체감한 적은 없었는데, 이번 프로젝트를 통해 fetch함수를 작성할 때도, 데이터를 수정하거나 변경하고 싶을 때도, back-end 팀원들과의 소통이 정말 중요하다는 것을 체감했다.
Front-end에서는 하나만 변경해도 될 것들이 back-end에서는 여러 테이블이 연결되어있어 복잡할 수도 있고, 아예 수정이 불가능 할 수도 있기 때문이었다. 그리고 중간에 팀원간에 발생한 착오 때문에 화면에서 뿌려지는 데이터가 예상과 달랐을 때, 소통의 중요성과 문서화의 중요성을 절실히 깨닫게 되었다.
의견을 합치시킬 수 있는 개발자
팀에서 내가 리더다! 라고 맡은 적은 없지만, 미팅에서나 의견을 조정하는 과정에서 리딩했던 적이 많았다.
매일 진행되는 미팅을 주도해가면서 여러가지로 나뉜 팀원들의 의견을 하나의 효율적인 방안으로 합치시켰고, 어떠한 방법을 채택할지에 대해 의견을 제시하고, 추진하기도 하였다.
이러한 과정을 통해 팀원들의 의견을 이끌어내는 방법에 대해 배울 수 있었고, 효과적인 토론 방법, 시간을 효율적으로 사용하여 미팅하는 방법에 대해서도 배울 수 있었다.
아직 많이 부족하지만, 중간중간 잘 이끌어가줘서 감사하다는 말을 해주신 우리 팀원들과, 나에게 PM도 굉장히 잘 어울린다며 칭찬해주신 팀원께 진심으로 감사하다고 말해주고 싶다.
또 하나의 변수, 체력
짧은 기간안에 완성을 해야하기 때문에 나와 팀원들은 매일 새벽까지 작업했다. 그러다보니 체력적으로 정말 힘들었다. 그것을 이겨내기 위해 힘들지만 중간중간 꼭 1시간씩 운동을 하였고, 스트레칭도 빼먹지 않고 하였다. 그리고 쉴 수 있는 시간에는 꼭 쉼으로써 체력을 보충하여 프로젝트를 완성할 수 있었다.
팀원이란 나의 전우
프로젝트를 시작할 때, 우리는 서먹했다. 비대면으로 진행하는 부트캠프이기 때문에 더더욱 그랬던 것 같다. 하지만 매일 같이 zoom이나 google meet을 켜놓고 같이 작업을 하면서 에러를 해결하기 위해 함께 머리를 맞대고 고민하기도하고, 중간중간에는 얘기를 나누고 웃기도 하면서 점점 끈끈해져갔다. 그렇게 친밀감이 높아갈 때 쯤, 마지막 발표영상을 찍기위해 스터디카페에서 만났던 날, 친밀감이 더더욱 많이 상승했던 것 같다. 오후 1시부터 자정까지 정말 긴 시간동안 스터디카페에서 함께 작업했지만, 힘들었던 것보다는 즐거웠던 마음이 더 컸다. 힘들었던 2주동안 함께 같은 목표를 향해 나아가며, 서로의 문제를 같이 고민해주었던 나의 팀원들. 정말 멋지고, 고생했다고 말해주고싶다.

'회고록' 카테고리의 다른 글
| [JUSTCODE] 1, 2차 프로젝트 리팩토링 (2) | 2022.11.29 |
|---|---|
| [JUSTCODE] 2차 프로젝트 회고 (2) | 2022.11.29 |
| [JUSTCODE] 1개월차 회고 (0) | 2022.11.13 |
| [JUSTCODE] pre-course 2주차 회고 (2) | 2022.10.15 |
| [JUSTCODE] pre-course 1주차 회고 (2) | 2022.10.11 |