커피를 좋아하는 현대인이라면 손쉽게 로그인하여 커피와 관련된 상품을 판매하고 일상을 공유할 수 있는 SNS 서비스입니다. 커피와의 일상을 게시글로 공유하며 좋아요와 댓글을 통해 사용자와 소통합니다. 다양한 사람들을 팔로우하고, 마음에 드는 피드가 있다면 '좋아요'를 누르거나 댓글을 달 수도 있습니다. 또한, 다른 사용자와 채팅창을 이용해 즐거운 대화도 나눌 수 있습니다.
🧷 배포 URL
김민영 | 김지수 | 김태희 | 채지훈 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
🔗 BradleyyKim | 🔗 jsk3342 | 🔗 greenT-Hee | 🔗 jihoon-chae |
바닐라 자바스크립트를 통해 라우팅 활용 및 상태관리, 비동기 통신 개념 등 기초적인 활용을 학습하고 구체적인 이해를 목표로 제작
- 빠른 기능 구현을 목표
- 유저와의 상호작용이 큰 프로젝트에서 동적인 UI의 필요성 체감하기 위함
- 웹 서비스의 규모의 커짐에 따라 리액트 컴포넌트의 중요성을 직접 확인하기 위함
- DOM 이벤트의 복잡성을 직접 체감하면서 virtual DOM의 필요성을 체감하기 위함
- Front-End : HTML CSS JavaScript Sass
- Back-End : 제공된 API 사용
- 버전 관리 및 이슈 : Github, Jira, Slack
- 🔗 회의록: Github Wiki, Notion
- 🔗 merge convention, commit convention, prettier 사용
- 디자인 : Figma, Illustrator
Jira | Slack |
---|---|
![]() |
![]() |
.
├──📁 src
├──📁 pages
| └── ...html
├──📁 css
├──📁 img
└──📁 js
- splash 구현
- 로그인 기능 구현
- 회원가입 구현
- 프로필 수정 구현
- 홈피드 구현
- 게시글 등록(upload)
- 텍스트 입력 및 업로드 구현
- 여러 이미지 업로드 구현
- POST요청으로 API에 데이터 저장
- 게시글 상세 페이지(postDetail)
- myProfile 페이지에서 이미지 클릭 시 postId값을 불러와 상세 페이지 UI 구현
- API에 저장된 개인 로그인 토큰을 이용해 GET요청을 통해 UI 구성
- 댓글 UI 구현
- 댓글 작성 기능(commentLoad)
- 댓글 작성 시 reload를 통해 댓글 실시간 추가 구현
- 검색페이지(search)
- 검색 결과 박스 실시간 구현 예정
-
상품등록 (product)
- 이미지 업로드 기능 구현
- 상품 데이터 POST 요청
-
상품수정(productModification)
- 이미지 업로드 기능 구현
- 상품 데이터 GET요청/ PUT 요청
-
채팅방(chatRoom)
- 텍스트 입력후 전송 버튼 클릭시 채팅창에 렌더링
- 이미지 업로드시 채팅창에 렌더링
-
myProfile & yourProfile
- 유저 정보, 상품&게시글 리스트 GET
-
404, 공용 및 프로필 모달, 삭제, 로그아웃 기능 구현
-
follwing & follower
- 팔로잉/ 팔로우 API GET, DELETE, POST
- 팔로우 버튼 누르면 숫자 증감 반영
-
좋아요 기능
- 좋아요 API POST, DELETE 요청
-
일러스트 로고 디자인
- 카페인 마켓 캐릭터 로고 및 아이콘 제작
2022.06.09 ~ 2022.07.29
splash | 로그인 페이지 | 회원가입 페이지 |
---|---|---|
![]() |
![]() |
![]() |
홈 페이지 | 채팅 페이지 | 404 페이지 |
---|---|---|
![]() |
![]() |
![]() |
게시글 작성 페이지 | 게시물 상세 페이지 |
---|---|
![]() |
![]() |
게시글 수정 | 게시글 삭제 | 댓글 기능 |
---|---|---|
![]() |
![]() |
![]() |
마이 프로필 페이지 | 유어 프로필 페이지 | 팔로우/팔로잉 기능 |
---|---|---|
![]() |
![]() |
![]() |
로그아웃 페이지 | 프로필 수정 페이지 |
---|---|
![]() |
![]() |
상품 등록 페이지 & 상품 링크 이동 | 상품 수정 페이지 | 상품 삭제 페이지 |
---|---|---|
![]() |
![]() |
![]() |
-
문제 상황
- 하나의 html파일과 API로 yourProfile, myProfile & 나의 팔로잉/팔로워 리스트와 다른 유저의 팔로잉/팔로워 리스트 동시에 구현해야 함
- following/follower 리스트에서 프로필로 넘어갈 때, 해당 유저에 맞는 yourProfile을 보여줘야 함
- 토큰에서는 상대방의 accountname을 가져올 수 없다는 것과 클릭 했을 때 해당 유저의 accountname만을 정확하게 집어내지 못하고 있었음
-
원인 추론
- GET 요청 url뒤에 accountname을 넣어줘야 하는데, 나의 프로필은 로컬스토리지에서 getItem으로 사용하면 되지만 상대방 프로필 accountname을 가져오지 못했음
-
해결 방법
- Location (현재 URL) Property 중에서 query string이라고 불리는 search string을 사용하여 해결
- search string은 url에서
?
를 포함한 뒷부분을 string 형식으로 반환해주는 역할
실제 적용 코드)
followingList.html에서 유저를 선택할 때 넘어가는 a태그의 href 주소를
?accountname=유저의 accountname
붙여서 보냄link.setAttribute( 'href', '../pages/myProfile.html?accountname=' + resJson[i].accountname );
프로필 html로 넘어와서 상대방의 accountname을 location.search로 변수에 담아 사용
내 계정일 경우에는 로컬스토리지에서, 상대방 프로필이면 url을 query string값이 accountname에 들어가게 처리
-
location.search⇒
?accountname=유저아이디
-
location.search.replace(’?’,’’) ⇒
accountname=유저아이디
-
location.search.replace(’?’,’’).split(’=’) ⇒
['accountname', '유저아이디']
📌 userInfo.js (프로필 상단, 유저 정보 API GET 요청 코드)
const myAccountname = `${window.localStorage.getItem('accountname')}`;
const yourAccountname = location.search.replace('?', '').split('=')[1];
const accountname = yourAccountname ? yourAccountname : myAccountname;
//프로필 정보 보여주기
async function infoUser() {
try {
const res = await fetch(url + '/profile/' + accountname, {
method: 'GET',
..생략..}
}
- 마이프로필로/유어프로필과 팔로잉/팔로워가 서로 같은 html 파일을 공유하고 있어, 404 에러를 많이 겪게 됨
- JS에서 흔하게 사용하는 뒤로가기인
window.history.back()
또는window.history.go(-1)
만 썼을 때 에러 발생 - Location Porperty를 사용해서 예외처리로 Error 해결
const btnBack = document.querySelector('.btnBack');
btnBack.addEventListener('click', () => {
location.href = window.history.back();
});
- location pathname: 위치에 대한 URL의 경로를 포함하는 문자열
- 사용 의도: html 파일명을 뽑아서 조건식에 사용하기 위해
const locationPath = location.pathname.split('.')[0].split('/')[3];
const btnBack = document.querySelector('.btnBack');
btnBack.addEventListener('click', () => {
if (accountname == yourAccountname) {
location.href = `../pages/${locationPath}.html?accountname=${yourAccountname}`;
} else if (accountname == myAccountname) {
location.href = `../pages/${locationPath}.html`;
}else {
location.href = window.history.back();
}
});
- 팀원들과 가까운 거리에 거주하여, 물리적 허들이 없다는 장점을 통해 최소 주 2회 오프라인 만남을 통해 빠른 피드백으로 비용 절감
- 팀원 간 이슈가 발생했을 때, 대면을 통한 솔직한 대화를 통해 바로 해결하여 시간 절약
- 로그인 기능과 배포 페이지를 우선적으로 구현함으로써 시스템적으로 연결된 기능들을 빠르게 구현
- 압도적인 커밋 수 (over 760 commits)
- API와 비동기로 통신하는 로직에 대한 이해가 어려움
- 팀원 절반이 중간에 코로나에 걸린 건강 이슈
- 초반에 커밋 컨벤션이 혼재되어 사용된 이슈
- JIRA를 도입하여 1주일 단위로 스프린트에 할 일을 작성하여 원할한 일정 관리
- Slack과 Github 브랜치를 연결하여 팀원의 커밋을 확인하고, 어떤 기능을 작업하는 지 즉각 확인
- github 페이지를 초기에 빠르게 배포하여 지속적으로 QA테스트 진행하여 즉각적으로 트러블 슈팅 및 이슈를 해결
- 잘 이행된 부분
- 간단한 CI/CD action 기능으로 자동 배포 수행 및 개념 이해
- API 명세에 대해 구체적으로 미리 파악해 서버와 통신을 원활하게 유지
- 비동기 작업에 대한 이해를 통해 유연하게 이벤트를 동작
- 폴더 구조를 미리 지정해 파일 경로 문제 제거
- 시작 전 git branch 대한 이해를 통해 branch 구체적으로 세분화하여 에러 방지
- 팀원 간 페이지 별로 작업 진행하여 충돌 최소화
- 재사용성을 고려하여 javaScript 기능을 구현해 모듈화 리팩토링 단계를 위한 기반을 마련
- 개선 및 학습한 부분
- 기능 복잡도가 올라갈 수록 javaScript 기능별로 구현하는 것이 더 낫다는 결론에 도달
- 파일을 기능별로 세분화하여 라우팅 주소 변경
- MVC 패턴에 대해 학습하여 모듈화 패턴에 맞추어 프로젝트 아키텍쳐를 구현
- 추상화 단계에 따라 함수를 분류 및 기능별 모듈화 작업
- 기능 복잡도가 올라갈 수록 javaScript 기능별로 구현하는 것이 더 낫다는 결론에 도달