Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

토이프로젝트2 6조 #1

Open
wants to merge 264 commits into
base: main
Choose a base branch
from
Open

토이프로젝트2 6조 #1

wants to merge 264 commits into from

Conversation

YongYong21
Copy link

@YongYong21 YongYong21 commented Nov 16, 2023

참교6조 CHWIMIMATE

1

CHIWIMIMATE 배포 레포

🧑🏻‍💻 프로젝트 소개

저희 참교6조는 Toy Project2 프로젝트에서 CHWIMIMATE라는 사이트를 개발하였으며 본 사이트는 취미가 맞는사람들끼리 채팅을 하여 친목을 쌓는 프로그램입니다.

🔐 테스트 계정 id, pw

id: admin
/
pw: test1234

[필수 구현사항]

✅ useState 또는 useReducer를 활용한 상태 관리 구현

✅ Sass, styled-component, emotion, Chakra UI, tailwind CSS 등을 활용한 스타일 구현

✅ react 상태를 통한 CRUD 구현

✅ 상태에 따라 달라지는 스타일 구현

✅ custom hook을 통한 비동기 처리 구현

✅ 유저인증 시스템(로그인, 회원가입) 구현

✅ jwt등의 유저 인증 시스템 (로그인, 회원가입 기능)

✅ 소켓을 이용한 채팅 구현

[선택 구현사항]

✅ typescript를 활용한 앱 구현

토이2 프로젝트 설명

🍋 소켓 기반 채팅앱

주어진 API와 소켓을 분석해 어떤 프로젝트를 진행/완성할 것인지 팀 단위로 자유롭게 결정하고 만들어보세요.
과제 수행 및 리뷰 기간은 별도 공지를 참고하세요!

과제 수행 및 제출 방법

Y_FE_Toy2_{팀명}

E.g, Y_FE_Toy2_GYOHEON
  1. 현재 저장소를 로컬에 클론(Clone)합니다.
  2. 자신의 팀명으로 브랜치를 생성합니다.(구분 가능하도록 팀명을 꼭 파스칼케이스로 표시하세요, git branch Y_FE_Toy2_Team13)
  3. 자신의 팀명 브랜치에서 과제를 수행합니다.
  4. 과제 수행이 완료되면, 자신의 팀명 브랜치를 원격 저장소에 푸시(Push)합니다.(main 브랜치에 푸시하지 않도록 꼭 주의하세요, git push origin Y_FE_Toy2_Team13)
  5. 저장소에서 main 브랜치를 대상으로 Pull Request 생성하면, 과제 제출이 완료됩니다!(E.g, main <== Y_FE_Toy2_Team13)
  6. Pull Request 링크를 LMS로도 제출해 주셔야 합니다.
  7. main 혹은 다른 사람의 브랜치로 절대 병합하지 않도록 주의하세요!
  8. Pull Request에서 보이는 설명을 다른 사람들이 이해하기 쉽도록 꼼꼼하게 작성하세요!
  • 과제 수행 및 제출 과정에서 문제가 발생한 경우, 바로 담당 멘토나 강사에게 얘기하세요!

  • 백엔드 서버에 문제가 생겼을 경우, 바로 슬랙의 GyoHeon Lee에게 연락하세요!

필수 구현 사항

  • useState 또는 useReducer를 활용한 상태 관리 구현
  • Sass, styled-component, emotion, Chakra UI, tailwind CSS 등을 활용한 스타일 구현
  • react 상태를 통한 CRUD 구현
  • 상태에 따라 달라지는 스타일 구현
  • custom hook을 통한 비동기 처리 구현
  • 유저인증 시스템(로그인, 회원가입) 구현
  • jwt등의 유저 인증 시스템 (로그인, 회원가입 기능)
  • 소켓을 이용한 채팅 구현

선택 구현 사항

  • Next.js를 활용한 서버 사이드 렌더링 구현
  • typescript를 활용한 앱 구현
  • storybook을 활용한 디자인 시스템 구현
  • jest를 활용한 단위 테스트 구현

추가 사항

  • api들의 응답 데이터들을 일부러 파편화 해두었습니다!
  • api들 간의 데이터를 조합하여 이상적인 구조를 만들어보세요.

예시 프로젝트

private-messaging-part-1-chat-ab610e9e03738ad37f7b0fb55c771087

API 사용법

  • 모든 network 요청(Request) headers에 아래 정보가 꼭 포함돼야 합니다!
  • serverId는 팀마다 개별 전달됩니다.
  • 확인할 수 없는 사용자나 팀의 DB 정보는 임의로 삭제될 수 있습니다!
{
  "content-type": "application/json",
  "serverId": "nREmPe9B"
}

기본 데이터 구조

user

interface User {
  id: string;
  password: string;
  name: string;
  picture: string;
  chats: string[]; // chat id만 속합니다.
}

chat

interface Chat {
  id: string;
  name: string;
  isPrivate: boolean;
  users: string[];
  messages: Message[]; // message 객체가 속합니다.

  updatedAt: Date;
}

message

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 중복 체크

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

  • socket.io 의 사용을 추천드립니다.
  • Socket 연결시에도 headers는 유지해야 합니다.

기본 연결

io(`https://fastcampus-chat.net/chat?chatId=${chatId}`, {
  extraHeaders: {
    Authorization: "Bearer <accessToken>",
    serverId: "test"
  }
});

emit Event(client -> server)

example

socket.emit("message-to-server", text);

message-to-server

  • 같은 방에 있는 사람들에게 메세지를 전달합니다.

요청 데이터

type RequestData: string;

fetch-messages

  • 이전 대화 목록을 불러옵니다.
  • messages-to-client로 데이터를 받을 수 있습니다.

요청 데이터

  • 없음

users

  • 접속 상태인 유저 목록을 불러옵니다.
  • users-to-client로 데이터를 받을 수 있습니다.

요청 데이터

  • 없음

on Event(server -> client)

example

socket.on("message-to-client", (messageObject) => {
  console.log(messageObject);
});

message-to-client

  • 같은 방에 있는 사람들에게 메세지를 전달합니다.

응답 데이터

interface ResponseData {
  id: string;
  text: string;
  userId: string; // 메세지를 보낸 사람의 id
  createdAt: Date;
}

messages-to-client

  • 이전 대화 목록을 불러옵니다.

응답 데이터

interface Message {
  id: string;
  text: string;
  userId: string; // 메세지를 보낸 사람의 id
  createdAt: Date;
}

interface ResponseData {
  messages: Message[];
}

join

  • 같은 방에 새로운 사람이 들어오면 모든 유저의 정보를 다시 받습니다.

응답 데이터

interface ResponseData {
  users: string[]; // 참여자들 id
  joiners: string[]; // 새로운 참여자 id
}

leave

  • 같은 방에 사람이 나가면 모든 유저의 정보를 다시 받습니다.

응답 데이터

interface ResponseData {
  users: string[]; // 참여자들 id
  leaver: string; // 나간 사용자 id
}

users-to-client

  • 접속 상태인 유저 목록을 불러옵니다.

응답 데이터

interface ResponseData {
  user: string[]; // 참가자들 id
}

server 연결

io(`https://fastcampus-chat.net/server`, {
  extraHeaders: {
    Authorization: "Bearer <accessToken>",
    serverId: "test"
  }
});

emit Event(client -> server)

example

socket.emit("users-server");

users-server

  • 같은 serverId를 사용하는 online 사용자를 불러옵니다.
  • users-server-to-client로 데이터를 받을 수 있습니다.

요청 데이터

  • 없음

on Event(server -> client)

example

socket.on("message-to-client", (messageObject) => {
  console.log(messageObject);
});

users-server-to-client

  • 같은 serverId를 사용하는 접속 상태인 유저 목록을 불러옵니다.

응답 데이터

interface ResponseData {
  user: string[]; // 참가자들 id
}

invite

  • 새로운 채팅방 생성시 해당 채팅방 유저에게 채팅방 정보를 전송합니다.
  • 기존 채팅방에 유저 초대시 초대된 유저에게 채팅방 정보를 전송합니다.

응답 데이터

interface ResponseData {
  id: string;
  name: string;
  users: string[]; // 참여자들 id
  isPrivate: boolean;
  updatedAt: Date;
}

new-chat

  • 새로운 대화방이 생긴 경우 (not private) 서버(팀에서 사용하는 serverId)의 참여자들에게 이를 전달합니다.

응답 데이터

interface ResponseData {
  id: string;
  name: string;
  users: string[]; // 참여자들 id
  isPrivate: boolean;
  updatedAt: Date;
}

🧑🏻‍💻 Contributor

@YongYong21 (박용희) : 프로필 레이아웃+파이어베이스설계

@KittelLee (이진욱) : 모달+레이아웃, 채팅

@tkyoun0421 (윤태관) : 채팅, 로그인, 회원가입

@furaha707 (이예인) : 레이아웃, 채팅

@mysdpy30s (김미정) : 컴포넌트 레이아웃, 채팅


🧑🏻‍💻 기술 스택

Enviroment & Config

UI Tools

Development & FrontEnd

Deploy

Communication


🧑🏻‍💻 파일 구조

CHWIMIMATE
├── src/
│   ├── assets/
|   |    ├── fonts/
|   |    └── images/
|   |
│   ├── components/
│   │    ├── Chat/
│   │    ├── FormInputBtn/
│   │    ├── Loader/
│   │    ├── Login/
│   │    ├── Main/
│   │    ├── Profile/
│   │    ├── SearchInput/
│   │    ├── SignUp/
|   |    ├── ModalPlus.tsx
|   |    ├── ModalHamburger.tsx
|   |    ├── PageNotFount.tsx
|   |    ├── Logout.tsx
|   |    ├── Layout.tsx
│   │    ├── Header.tsx
│   │    └── Footer.tsx
│   │
|   ├── firebase/
|   ├── hooks/
│   ├── pages/
│   ├── style/
│   ├── utils/
│   ├── App.tsx
│   └── index.tsx
│
├── node_modules/
├── .eslintrc.cjs
├── .gitignore
├── .prettierrc
├── index.html
├── package-lock.json
├── package.json
├── README.md
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
└── vite.config.ts

🧑🏻‍💻 화면 구성

메인페이지+헤더

mate1

메인페이지+섹션

mate2

취미메이트 만들기&확인하기

mate3

채팅

mate4

🧑🏻‍💻 팀 소개

박용희 이예인 프로필 김미정 프로필 이진욱 프로필 윤태관 프로필 구영표 멘토님
박용희
Frontend
이예인
Frontend
김미정
Frontend
이진욱
Frontend
윤태관
Frontend
구영표 멘토님
멘토링

🧑🏻‍💻 개발 기간 : 2주(13일) 23.11.06 ~ 23.11.16

tkyoun0421 and others added 30 commits November 7, 2023 15:36
공용 스타일 수정 및 추가, 폰트 추가
로그인 페이지 퍼블리싱 완료
회원가입 페이지 퍼블리싱 완료
Font 에러 해결 및 styled-reset 추가 / ButtonStyle Interface 수정
TaePoong719 pushed a commit that referenced this pull request Nov 16, 2023
TaePoong719 pushed a commit that referenced this pull request Nov 16, 2023
Feat : 오픈채팅방 데이터 받아와서 리스트에 뿌려주기 pr
2YH02 added a commit that referenced this pull request Nov 17, 2023
useFireFetch, useFetch, index.css 수정
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
커스텀 훅 타입 및 환경변수 추가, 예시 페이지 소켓통신 추가
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
useFetch 에러 리턴 추가,  예시 페이지 서버채팅 추가
2YH02 added a commit that referenced this pull request Nov 17, 2023
유저초대, 초대 알림 기능
2YH02 added a commit that referenced this pull request Nov 17, 2023
게임 생성 시 게임 페이지로 이동
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
게임 입장 시 콘솔에 데이터 출력
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
토스트 팝업 ui 데이터 연결, 초대 거절 수락 기능 추가
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
유저 초대 목록 표시 오류 수정
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
소켓 통신 useSocket 추가, 토스트 알람 UI 일부 수정
2YH02 added a commit that referenced this pull request Nov 17, 2023
2YH02 added a commit that referenced this pull request Nov 17, 2023
2YH02 added a commit that referenced this pull request Nov 17, 2023
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
게임 생성 시 이모티콘 추가 기능, 로딩 ui 추가
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
초대 팝업 소켓 통신 코드 수정, 메시지 중복 문제 해결, 소켓 연결 모듈화(useSocket 제거)
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
[FIX] 초대 인원 제한, 게임 초대 수락 시 게임 입장
2YH02 pushed a commit that referenced this pull request Nov 17, 2023
초대 팝업 오류 수정, 유저 정보 리코일로 가져오기
2YH02 added a commit that referenced this pull request Nov 17, 2023
게임 입장 시 파이어베이스 업데이트
seungjun222 pushed a commit that referenced this pull request Nov 17, 2023
dbstjrals pushed a commit that referenced this pull request Nov 17, 2023
…on-bar

네비게이션 바를 구현한다.
JeongMin83 added a commit that referenced this pull request Nov 17, 2023
jseo9732 added a commit that referenced this pull request Nov 18, 2023
TALK-22--feat/layout/styles 공통 styles 파일 수정 및 추가
Yamyam-code pushed a commit that referenced this pull request Nov 18, 2023
noSPkeepgoing added a commit that referenced this pull request Nov 18, 2023
✨Feat: add pocketRequest.ts in api
noSPkeepgoing pushed a commit that referenced this pull request Nov 18, 2023
@marshallku marshallku self-assigned this Nov 20, 2023
Copy link
Member

@marshallku marshallku left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다!
반복되는 코멘트는 생략했으니, 본인 코드에 달린 리뷰가 아니더라도 확인 부탁드립니다!

  1. 일관성 있는 코드 작성하기 (작명, 로직의 구현 등)
  2. 비동기 호출에서 일어나는 오류 처리하기
  3. 반복되는 로직은 공통화하기
  4. 컴포넌트 작게 쪼개기

앞으로 진행하는 프로젝트들에서는, 위 항목들에 대해 조금 더 고민해보시면 좋을 것 같습니다!
짧은 기간동안 고생 많으셨고, 앞으로 프로젝트도 화이팅입니다~

Comment on lines +13 to +14
"@types/react-modal": "^3.16.3",
"@types/react-slick": "^0.23.12",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

devDependencies에 포함되어야할 패키지들같네요~


export const ThemeContext = createContext<ContextProps>({
theme: lightTheme,
toggleTheme: () => {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 의미로 여기서 Empty object를 반환하는 건가요?
{} 타입은 'nullish하지 않은 모든 값'을 의미하게 됩니다. 좀 더 정확한 타입을 지정해주면 좋을 것 같습니다!

}
]);

const App: React.FC = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React.FC 타입을 사용하면 defaultProps나 children을 원하는대로 제어하기 힘들고, 작성해야하는 코드 양 자체도 많아지다보니 저는 선호하지 않는 편입니다.
혹시 사용하게되신 이유가 있을까요? 저도 사용하지 않은지 오래돼다보니 새로운 장점이 생긴 게 있는지 궁금하네요.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 건 fantasticon같은 걸 사용해서 아이콘 폰트로 제작해보거나, svg의 fill을 변경해봐도 괜찮았을 것 같네요!

Comment on lines +19 to +23
export interface User {
id: string;
name: string;
picture: string;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 ChatRoom과 무관한 타입은 다른 타입을 관리하는 파일에서 선언되었어도 괜찮지 않았을까요?

fetchData();
}
}, [accessToken, roomId]);
// TODO : 여기에 의존성배열로 chatRoom 을 넣어줘야하는데 그러면 너무 렌더링이 많아져서 또 채팅방 내역을 잘 못 불러옴
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setChatRoom을 하는 useEffectchatRoom을 dependency array에 넣으면 렌더링이 매번 일어나는 게 당연할 것 같은데, 혹시 추가가 필요한 이유가 있을까요?

Comment on lines +80 to +82
const minutesDifference = Math.floor(timeDifference / (1000 * 60));
const hoursDifference = Math.floor(minutesDifference / 60);
const daysDifference = Math.floor(hoursDifference / 24);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magic number 사용은 가급적 지양해주시면 좋을 것 같습니다!

Comment on lines +17 to +18
default:
return "190px";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개발 환경일 땐 에러를 throw해주면 좀 더 직관적으로 size를 추가해야한다는 걸 알 수 있을 것 같습니다!

Comment on lines +7 to +8
font-family: 'PretendardVariable';
src: url("../assets/fonts/PretendardVariable.woff2") format('woff2-variations');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fallback 폰트를 추가하시고, font-display: swap을 추가해 폰트를 불러오는 중에 시스템 폰트를 출력할 수 있도록 해보면 좋을 것 같네요.

Comment on lines +18 to +21
} else {
const hoursDifference = Math.floor(minutesDifference / 60);
updatedAtString = `${hoursDifference}시간 전`;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

60분 뒤로는 모두 $DIFF시간 전으로 출력되는데, 기간이 오래되는 것을 감안하면 $DIFF년 전정도 단위까지 추가하거나, 날짜를 포매팅해서 출력하도록 했으면 좋지 않았을까요?

Copy link

@tkyoun0421 tkyoun0421 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드 리뷰 감사합니다! 구영표 멘토님

setLoading(true);

setTimeout(() => {
postApi(LOGIN_API_URL, loginData)
Copy link

@tkyoun0421 tkyoun0421 Dec 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 API 요청에선 토큰을 필요로 하지 않아서 따로 작성했지만 좀 더 알아보니 axios에서 interceptor 라는 것을 지원한다는 것을 알게 되었습니다.
다음부터는 해당 기능을 사용해서 코드를 짜보겠습니다.

Comment on lines +33 to +34
sessionStorage.setItem("refreshToken", refreshToken);
sessionStorage.setItem("userId", loginData.id);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로컬 스토리지에 저장하게 되면 브라우저를 종료하더라도 로그인 상태가 유지되어서 제가 생각하는 UX 상으로는 자동 로그인을 체크하지 않으면 로그아웃이 되는게 좀 더 좋은 방향이라고 판단해서 세션에 저장하였습니다.

.catch((error) => {
console.error(error);
setErrorMessage("아이디와 비밀번호를 확인해주세요.");
setLoading(false);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 공통된 코드는 finally로 옮겨 선언하겠습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants