ID: test5
PW: 11111
서지수 | 김지민 | 박준규 | 어준혁 | 윤지영 | 구영표 멘토님 |
- main: 배포용 브랜치
- develop : 개발용 브랜치
- TALK 개별 개발용 브랜치
✅ useState
, useReducer
를 활용한 상태 관리 구현
✅ Sass
또는 styled-component
를 활용한 스타일 구현
✅ react
상태를 통한 CRUD 구현
✅ 상태에 따라 달라지는 스타일 구현
✅ custom hook
을 통한 비동기 처리 구현
✅ 유저인증 시스템(로그인, 회원가입) 구현
✅ jwt
등의 유저 인증 시스템 (로그인, 회원가입 기능)
✅ 소켓을 이용한 채팅 구현
✅ Next.js
를 활용한 서버 사이드 렌더링 구현
✅ typescript
를 활용한 앱 구현
Feat | 새로운 기능 추가 |
---|---|
Design | CSS 등 사용자 UI 디자인 변경 |
Fix | 버그 수정 |
Docs | 문서 수정 |
Style | 코드 포맷팅, 세미콜론 누락 |
Refactor | 코드 리팩토링 |
Test | 테스트 코드, 리팩토링 테스트코드 추가 |
Chore | 빌드 업무 수정, 패키지 매니저 수정 |
Comment | 필요한 주석 추가 및 변경 |
Rename | 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우 |
Remove | 파일을 삭제하는 작업만 수행한 경우 |
📦Talkhaja
┣ 📂@types
┃ ┣ 📜types.ts
┃ ┗ 📜user.ts
┣ 📂apis
┃ ┣ 📜Auth.ts
┃ ┣ ...
┃ ┗ 📜userListAPI.ts
┣ 📂components
┃ ┣ 📂Chat
┃ ┃ ┣ 📂ChatLoading
┃ ┃ ┃ ┣ 📜ChatLoading.module.scss
┃ ┃ ┃ ┗ 📜ChatLoading.tsx
┃ ┃ ┣ ...
┃ ┃ ┗ 📜index.ts
┃ ┣ 📂Common
┃ ┃ ┣ 📂Header
┃ ┃ ┃ ┣ 📜Header.module.scss
┃ ┃ ┃ ┗ 📜Header.tsx
┃ ┃ ┣ ...
┃ ┃ ┗ 📜index.ts
┣ 📂constants
┃ ┗ 📜userLoinState.ts
┣ 📂hooks
┃ ┣ 📜useConnectServerSocket.tsx
┃ ┗ 📜useOnClickOustside.tsx
┣ 📂pages
┃ ┣ 📂chat
┃ ┃ ┗ 📜[chatId].tsx
┃ ┣ 📂chat-list
┃ ┃ ┣ 📜all.tsx
┃ ┃ ┗ 📜my.tsx
┃ ┣ 📜_app.tsx
┃ ┣ 📜_document.tsx
┃ ┣ 📜index.tsx
┃ ┣ 📜login.tsx
┃ ┣ 📜mypage.tsx
┃ ┗ 📜signup.tsx
┣ 📂public
┃ ┣ 📂fonts
┃ ┃ ┣ 📜NotoSansKR-Bold.ttf
┃ ┃ ┣ ...
┃ ┃ ┗ 📜fonts.css
┃ ┣ 📂images
┃ ┃ ┣ 📜Spin-1s-200px.gif
┃ ┃ ┣ 📜Talkhaja.svg
┃ ┃ ┗ 📜logo.png
┃ ┣ 📜favicon.ico
┃ ┣ 📜next.svg
┃ ┗ 📜vercel.svg
┣ 📂stores
┃ ┗ 📂atoms
┃ ┃ ┣ 📜nav.atoms.ts
┃ ┃ ┣ 📜user.atoms.ts
┃ ┃ ┗ 📜userTokenState.ts
┣ 📂styles
┃ ┣ 📜_mixin.scss
┃ ┣ 📜_variables.scss
┃ ┣ 📜fonts.scss
┃ ┣ 📜index.scss
┃ ┗ 📜normalize.scss
┣ 📂utils
┃ ┣ 📜authorizeFetch.ts
┃ ┣ ...
┃ ┗ 📜tokenManager.ts
┣ 📜.env
┣ 📜.eslintrc.json
┣ 📜.gitignore
┣ 📜.prettierrc
┣ 📜README.md
┣ 📜next-env.d.ts
┣ 📜next.config.js
┣ 📜package-lock.json
┣ 📜package.json
┗ 📜tsconfig.json
- 최근 채팅 시간에 따른 '방금', 'n분 전', 'n시간 전', 'n일 전' 표시
제공 된 API
## API 사용법- 모든 network 요청(Request)
headers
에 아래 정보가 꼭 포함돼야 합니다! - serverId는 팀마다 개별 전달됩니다.
- 확인할 수 없는 사용자나 팀의 DB 정보는 임의로 삭제될 수 있습니다!
{
"content-type": "application/json",
"serverId": "nREmPe9B",
}
interface User {
id: string;
password: string;
name: string;
picture: string;
chats: string[]; // chat id만 속합니다.
}
interface Chat {
id: string;
name: string;
isPrivate: boolean;
users: string[];
messages: Message[]; // message 객체가 속합니다.
updatedAt: Date;
}
interface Message {
id: string;
text: string;
userId: string;
createdAt: Date;
}
사용자가 id
에 종속되어 회원가입합니다.
- 사용자 비밀번호는 암호화해 저장합니다.
- 프로필 이미지는 url or base64 형식이어야 합니다.
- 프로필 이미지는 1MB 이하여야 합니다.
curl https://fastcampus-chat.net/signup
\ -X 'POST'
요청 데이터 타입 및 예시:
interface RequestBody {
id: string // 사용자 아이디 (필수!, 영어와 숫자만)
password: string // 사용자 비밀번호, 5자 이상 (필수!)
name: string // 사용자 이름, 20자 이하 (필수!)
picture?: string // 사용자 이미지(url or base64, under 1MB)
}
{
"id": "abcd",
"password": "********",
"name": "GyoHeon",
"picture": "https://avatars.githubusercontent.com/u/66263916?v=4"
}
응답 데이터 타입 및 예시:
interface ResponseValue {
message: title
}
{
"message": "User created"
}
id
중복 체크를 합니다.
curl https://fastcampus-chat.net/check/id
\ -X 'POST'
요청 데이터 타입 및 예시:
interface RequestBody {
id: string // 사용자 아이디 (필수!, 영어와 숫자만)
}
{
"id": "abcd",
}
응답 데이터 타입 및 예시:
interface ResponseValue {
isDuplicated: boolean
}
{
"isDuplicated": false
}
- 발급된
accessToken
은 7일 후 만료됩니다.
curl https://fastcampus-chat.net/login
\ -X 'POST'
요청 데이터 타입 및 예시:
interface RequestBody {
id: string // 사용자 아이디 (필수!)
password: string // 사용자 비밀번호 (필수!)
}
{
"id": "abcd",
"password": "********"
}
응답 데이터 타입 및 예시:
interface ResponseValue {
accessToken: string // 사용자 접근 토큰
refreshToken: string // access token 발급용 토큰
}
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)"
}
id
중복 체크를 합니다.
curl https://fastcampus-chat.net/auth/me
\ -X 'GET'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
- 없음
응답 데이터 타입 및 예시:
interface ResponseValue {
auth: boolean;
user?: User;
}
interface User {
id: string;
name: string;
picture: string;
}
{
"auth": true,
"user": {
"id": "test1",
"name": "abcde",
"picture": "https://avatars.githubusercontent.com/u/42333366?v=4"
}
}
curl https://fastcampus-chat.net/refresh
\ -X 'POST'
요청 데이터 타입 및 예시:
interface RequestBody {
refreshToken: string // access token 발급용 토큰
}
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)"
}
응답 데이터 타입 및 예시:
interface ResponseValue {
accessToken: string // 사용자 접근 토큰
}
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)",
}
- 프로필 이미지는 url or base64 형식이어야 합니다.
- 프로필 이미지는 1MB 이하여야 합니다.
curl https://fastcampus-chat.net/user
\ -X 'PATCH'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
interface RequestBody {
name?: string // 새로운 표시 이름
picture?: string // 사용자 프로필 이미지(url or base64)
}
{
"name": "abcde",
"picture": "https://avatars.githubusercontent.com/u/42333366?v=4"
}
응답 데이터 타입 및 예시:
interface ResponseValue {
messgae: string
}
{
"message": "User updated"
}
- 특정 유저를 조회합니다.
curl https://fastcampus-chat.net/user?userId=${userId}
\ -X 'GET'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
- 없음
- 조회하고 싶은 id는 query string으로 사용합니다.
응답 데이터 타입 및 예시:
type ResponseValue = {
user: User;
}
interface User {
id: string;
name: string;
picture: string;
}
{
"user": {
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
}
}
- 현재 존재하는 모든 유저를 조회합니다.
curl https://fastcampus-chat.net/users
\ -X 'GET'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
- 없음
응답 데이터 타입 및 예시:
type ResponseValue = User[]
interface User {
id: string;
name: string;
picture: string;
}
[
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
]
curl https://fastcampus-chat.net/chat
\ -X 'POST'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
interface RequestBody{
name: string, // chat 이름
users: string[], // 참가자들 id(자신 미포함)
isPrivate?: boolean // 공개 비공개
}
{
"name": "test chat",
"users": ["user1", "user2"]
}
응답 데이터 타입 및 예시:
interface ResponseValue {
id: string,
name: string,
users: User[], // 자신을 포함한 참가자들 정보
isPrivate: boolean,
updatedAt: Date
}
interface User {
id: string;
name: string;
picture: string;
}
{
"id": "fasgadsfdsghssdlsdafasd",
"name": "test chat",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": false,
"updatedAt": "2023-11-01T08:23:39.850Z"
}
- 특정 id의 채팅을 조회합니다.
- isPrivate: true인 채팅방은 해당 채팅방 참가자만 볼 수 있습니다.
curl https://fastcampus-chat.net/chat/only?chatId=${chatId}
\ -X 'GET'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
- 없음
응답 데이터 타입 및 예시:
interface ResponseValue {
chat: Chat;
}
interface Chat {
id: string;
name: string;
users: User[]; // 속한 유저 정보
isPrivate: boolean;
latestMessage: Message | null;
updatedAt: Date;
}
interface User {
id: string;
name: string;
picture: string;
}
interface Message {
id: string;
text: string;
userId: string;
createAt: Date;
}
{
chat: {
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede",
"name": "chat room 1",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": false,
"updatedAt": "2023-10-31T13:18:38.216Z",
"latestMessage": null
}
}
- 현재 존재하는 모든 채팅을 조회합니다.
- isPrivate: true인 채팅방은 보이지 않습니다.
curl https://fastcampus-chat.net/chat/all
\ -X 'GET'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
- 없음
응답 데이터 타입 및 예시:
type ResponseValue = Chat[]
interface Chat {
id: string;
name: string;
users: User[]; // 속한 유저 정보
isPrivate: boolean;
latestMessage: Message | null;
updatedAt: Date;
}
interface User {
id: string;
name: string;
picture: string;
}
interface Message {
id: string;
text: string;
userId: string;
createAt: Date;
}
[
{
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede",
"name": "chat room 1",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": false,
"updatedAt": "2023-10-31T13:18:38.216Z",
"latestMessage": null
},
{
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8edj",
"name": "chat room 2",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": false,
"updatedAt": "2023-10-31T15:18:38.216Z",
"latestMessage": {
"id": "8f7f67bb-f1ab-4792-9678-0b8546adcb6f",
"text": "testtest444",
"userId": "test:test6",
"createdAt": "2023-11-06T11:15:50.588+00:00"
}
}
]
curl https://fastcampus-chat.net/chat
\ -X 'GET'
\ -H 'Authorization: Bearer <accessToken>'
- 내가 속한 모든 채팅을 조회합니다.
- isPrivate: true인 채팅방도 모두 보이게 됩니다.
요청 데이터 타입 및 예시:
- 없음
응답 데이터 타입 및 예시:
type ResponseValue = Chat[]
interface Chat {
id: string;
name: string;
users: User[]; // 속한 유저 id
isPrivate: boolean;
latestMessage: Message | null;
updatedAt: Date;
}
interface User {
id: string;
name: string;
picture: string;
}
interface Message {
id: string;
text: string;
userId: string;
createAt: Date;
}
[
{
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede",
"name": "chat room 1",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": true,
"updatedAt": "2023-10-31T13:18:38.216Z",
"latestMessage": null
},
{
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8edj",
"name": "chat room 2",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": false,
"updatedAt": "2023-10-31T15:18:38.216Z",
"latestMessage": {
"id": "8f7f67bb-f1ab-4792-9678-0b8546adcb6f",
"text": "testtest444",
"userId": "test:test6",
"createdAt": "2023-11-06T11:15:50.588+00:00"
}
}
]
curl https://fastcampus-chat.net/chat/participate
\ -X 'PATCH'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
interface RequestBody {
chatId: string;
}
{
"chatId": "f189ab25-5644-4d72-bd7c-0170ee9c8ede"
}
응답 데이터 타입 및 예시:
interface ResponseValue{
id: string;
name: string;
users: User[]; // 속한 유저 id
isPrivate: boolean;
updatedAt: Date;
}
interface User {
id: string;
name: string;
picture: string;
}
{
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede",
"name": "chat room 1",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": true,
"updatedAt": "2023-10-31T13:18:38.216Z"
}
curl https://fastcampus-chat.net/chat/leave
\ -X 'PATCH'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
interface RequestBody {
chatId: string;
}
{
"chatId": "f189ab25-5644-4d72-bd7c-0170ee9c8ede"
}
응답 데이터 타입 및 예시:
interface ResponseValue {
message: string;
}
{
"message": "Leave success"
}
curl https://fastcampus-chat.net/chat/invite
\ -X 'PATCH'
\ -H 'Authorization: Bearer <accessToken>'
요청 데이터 타입 및 예시:
interface RequestBody {
chatId: string;
users: string[]; // 초대할 유저 id
}
{
"chatId": "f189ab25-5644-4d72-bd7c-0170ee9c8ede",
"users": ["user1", "user2"]
}
응답 데이터 타입 및 예시:
interface ResponseValue{
id: string;
name: string;
users: User[]; // 속한 유저 정보
isPrivate: boolean;
updatedAt: Date;
}
interface User {
id: string;
name: string;
picture: string;
}
{
"id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede",
"name": "chat room 1",
"users": [
{
"id": "user1",
"name": "lgh",
"picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro"
},
{
"id": "user2",
"name": "ldj",
"picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro"
}
],
"isPrivate": true,
"updatedAt": "2023-10-31T13:18:38.216Z"
}
- socket.io 의 사용을 추천드립니다.
- Socket 연결시에도 headers는 유지해야 합니다.
io(`https://fastcampus-chat.net/chat?chatId=${chatId}`,
{
extraHeaders: {
Authorization: "Bearer <accessToken>",
serverId: "test",
},
})
socket.emit('message-to-server', text)
- 같은 방에 있는 사람들에게 메세지를 전달합니다.
요청 데이터
type RequestData: string;
- 이전 대화 목록을 불러옵니다.
messages-to-client
로 데이터를 받을 수 있습니다.
요청 데이터
- 없음
- 접속 상태인 유저 목록을 불러옵니다.
users-to-client
로 데이터를 받을 수 있습니다.
요청 데이터
- 없음
socket.on('message-to-client', (messageObject) => {
console.log(messageObject);
})
- 같은 방에 있는 사람들에게 메세지를 전달합니다.
응답 데이터
interface ResponseData {
id: string;
text: string;
userId: string; // 메세지를 보낸 사람의 id
createdAt: Date;
}
- 이전 대화 목록을 불러옵니다.
응답 데이터
interface Message {
id: string;
text: string;
userId: string; // 메세지를 보낸 사람의 id
createdAt: Date;
}
interface ResponseData {
messages: Message[];
}
- 같은 방에 새로운 사람이 들어오면 모든 유저의 정보를 다시 받습니다.
응답 데이터
interface ResponseData {
users: string[]; // 참여자들 id
joiners: string[]; // 새로운 참여자 id
}
- 같은 방에 사람이 나가면 모든 유저의 정보를 다시 받습니다.
응답 데이터
interface ResponseData {
users: string[]; // 참여자들 id
leaver: string; // 나간 사용자 id
}
- 접속 상태인 유저 목록을 불러옵니다.
응답 데이터
interface ResponseData {
user: string[]; // 참가자들 id
}
io(`https://fastcampus-chat.net/server`,
{
extraHeaders: {
Authorization: "Bearer <accessToken>",
serverId: "test",
},
})
socket.emit('users-server')
- 같은 serverId를 사용하는 online 사용자를 불러옵니다.
users-server-to-client
로 데이터를 받을 수 있습니다.
요청 데이터
- 없음
socket.on('message-to-client', (messageObject) => {
console.log(messageObject);
})
- 같은 serverId를 사용하는 접속 상태인 유저 목록을 불러옵니다.
응답 데이터
interface ResponseData {
user: string[]; // 참가자들 id
}
- 새로운 채팅방 생성시 해당 채팅방 유저에게 채팅방 정보를 전송합니다.
- 기존 채팅방에 유저 초대시 초대된 유저에게 채팅방 정보를 전송합니다.
응답 데이터
interface ResponseData {
id: string;
name: string;
users: string[]; // 참여자들 id
isPrivate: boolean;
updatedAt: Date;
}
- 새로운 대화방이 생긴 경우 (not private) 서버(팀에서 사용하는 serverId)의 참여자들에게 이를 전달합니다.
응답 데이터
interface ResponseData {
id: string;
name: string;
users: string[]; // 참여자들 id
isPrivate: boolean;
updatedAt: Date;
}
프로젝트 진행 중 가장 큰 어려움 중 하나는 제공된 API만으로 일반 사용자와 숙박업체를 운영하는 호스트 사용자를 구분할 수 없었다는 점이었습니다. API에서 제공하는 사용자 정보에는 'id', 'password', 'name', 'picture' 등의 기본적인 정보만 포함되어 있어, 숙박업체에 대한 구체적인 정보인 'location', 'address', 'detail' 등을 관리할 수 없었습니다.
이러한 한계로 인해, 호스트 사용자와 그들의 숙박업체 정보를 효과적으로 관리하고, 일반 사용자와 호스트 간의 일대일 채팅 기능을 구현하기 위해서는 추가적인 데이터 관리 시스템이 필요했습니다. 이에 따라, Firebase를 프로젝트에 도입하여 호스트 데이터를 별도로 관리하기로 결정했습니다.
Firebase를 사용하면서 고려한 주요 사항은 다음과 같습니다:
- 데이터 구조 설계: Firebase에 저장할 호스트 데이터의 구조를 설계하여, 'location', 'address', 'detail'과 같은 필요한 정보를 포함시켰습니다.
- 데이터 동기화: API를 통해 얻은 사용자 정보와 Firebase에 저장된 호스트 정보 간의 동기화를 어떻게 유지할지에 대한 고민이 필요했습니다. 특히, 호스트의 'id'를 기준으로 API의 사용자 정보와 Firebase의 호스트 정보를 연결하는 방식을 구현했습니다. 결국, Firebase를 도입함으로써 개별 호스트에 대한 상세 정보를 관리할 수 있는 구조를 마련하고, 제공된 API의 한계를 극복하여 프로젝트의 핵심 기능을 구현할 수 있었습니다.
급변하는 프론트엔드 세계에서 매 번 새로운 라이브러리와 새로운 프레임워크들을 익혀야 한다는 점이 항상 큰 부담이었습니다. 하지만 이번 프로젝트를 통해 그 부담을 많이 덜어 낼 수 있었던 것 같습니다. 프로젝트가 진행됨에 따라 다양한 개발 경험을 쌓을 수 있었고, 그 과정에서 각 라이브러리의 편리함을 직접 체험할 수 있었습니다. 새로운 라이브러리와 프레임워크는 빠르게 학습하고 정복해야 할 대상이 아닌 도움이 되는 도구로 인식하게 되었고, 이러한 마음가짐의 변화는 개발을 더욱 즐겁게 만들어 주었습니다. 또한 협업도구로 'zira'도 처음 사용해보았는데 github와의 연동을 통해 브랜치 관리와 작업상황 공유가 가능한 점이 좋았습니다. 기존 notion을 통해 단순히 역할 분배/작업상황을 기록 하는 것 보다 효율적이고 편리했던 것 같습니다.
NEXT.js를 본격적으로 사용해 본 적은 이번이 처음이었는데 생각보다 낯설지 않아서 좋았습니다. 하지만 낯설지 않았던 가장 큰 이유는 NEXT.js의 강점을 충분히 활용하지 못했기 때문 인 것 같습니다..😂 팀원 모두가 NEXT.js 프로젝트는 처음이었기 때문에, App router에 비해 더 간단하고 직관적인 Pages Router를 사용하였습니다. 하지만 App Router가 다양한 라우팅 요구사항과 상황에 더 유연하게 대처 가능하며, 성능면에서 더욱 좋다고 하여 다음에는 App Router를 사용해보고 싶습니다. 또한 다음 NEXT.js 프로젝트에서는 CSR/SSG/ISR/SSR 각각의 렌더링 방식의 특성과 장단점을 이해하고, 프로젝트를 진행하기 전에 렌더링 방식을 고려한 페이지 설계를 하고 싶습니다.
좋은 조장님과 팀원들, 그리고 멘토님을 만난 덕분에 프로젝트를 성공적으로 마무리 할 수 있었습니다. 단순히 역할을 나누어 각자의 기능을 개발하는 것이 아니라, 작업 상태와 결과를 보며 어떻게 하면 더 개선할 수 있을지 피드백을 주고받으며 많은 것을 보고 배울 수 있었습니다. 문제를 해결하기 위한 접근 방법에 대해서도 차근차근 고민해보고, 서로 방법을 공유해 나갔기 때문에 수정 작업도 즐겁게 할 수 있었습니다. 다시 한 번 멋진 팀원들께 감사 인사를 전하고 싶습니다.💖