PJH's live chat - Start & Setting

박정호·2023년 2월 5일
0

Live Chat Project

목록 보기
1/7
post-thumbnail

🚀 Start

기술 공부를 하는중 웹소켓에 대해 알게 되었다. 그리고 Socket.io를 이용하여 실시간 채팅 기능과 같은 양방향 통신을 구현할 수 있다는 사실을 알았다. 지금까지는 이와 관련된 프로젝트를 진행해본 적은 없었는데 마침 제로초님의 강의 중에 slack 앱을 클론코딩하며 만다는 실시간 채팅 프로젝트가 있어서 진행해보게 되었다.

  • WebSocket은 하나의 TCP 접속에 전이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜이다.

  • Socket.io는 WebSocket을 기반으로 JavaScript를 이용하여 편의기능이 많이 추가되어있으며, 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있도록 한 기술이다.


👍 Server와 DB는 이미 구현되어 있는 상태에서 프론트엔드 개발자의 초점에 맞춰 진행되고, CSS는 slack앱의 실제 CSS요소들을 가져와서 디자인하므로 내가 배우고 싶은 스킬들을 중점적으로 배우고 노하우를 얻을 수 있어서 좋을 것 같다.

💡 참고하자!
👉 WebSocket과 Socket.io
👉 [SOCKET] 📚 Socket.IO 사용 해보기
👉 Socket.io 공식문서



⚙️ Setting

🖥 Client

React 환경에서 CRA 없이 Webpack, Babel을 통하여 직접 커스터마이징한 환경을 만들 수 있고, TypeScript, ESLint, Prettier을 사용한다.

이미 사용해보았던 기술들이어서 환경설정은 손쉽게 구축할 수 있었다.

💡 CRA 개발환경은 구축이 편리하다는 장점이 있지만, 사용하지 않는 기능까지 포함하여 모듈의 크기를 크게 만든다는 단점이 있다.



👉 babel vs tsc

바벨7 이전

👉 TS > TS compiler > JS > Babel > JS 순서

웹팩을 통해 두개의 컴파일러( babel, tsc )를 함께 사용 가능하며, 웹팹 설정을 비틀어 *.ts 를 타입스크립트로 입력한 다음 결과를 바벨에 제공한다.

웹팩의 타입스크립트 로더에는 ts-loader, awesome-typscript-loader 가 있는데 awe~ 일부 작업의 부하로 컴파일 속도가 느리며, ts-loader는 많은 복잡한 캐시 로더를 함께 설정하여 사용해야 하는 불편함이 있다.

바벨7 이후

👉 바벨은 타입스크립트를 우선 js로 변경한다.

바벨 + 타입스크립트코드는 느린 컴파일 시간을 개선하였다.

준비가 되었을 때만 타입 오류를 확인한다. js로 우선 안정성 검사 하지 않고 컴파일한 다음 코드 실험이 끝나고 타입 검사를 진행한다.

TypeScript는 전체 프로젝트를 컴파일 하지만 Babel은 한번에 하나의 파일만 컴파일 한다.

💡 참고하자!
👉 바벨과 타입스크립트의 아름다운 결혼



👉 Path Alias 설정

webpack과 TSConfig에서 주요 페이지 경로에 대해 alias를 설정하여, 경로에 깔끔하게 접근가능하다.

👎 import {...} from ../../../../../layout/App

👍 import {...} from @layouts/App

webpack.confing.ts

const config: Configuration = {
 	...
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    alias: {
      '@hooks': path.resolve(__dirname, 'hooks'),
      '@components': path.resolve(__dirname, 'components'),
      '@layouts': path.resolve(__dirname, 'layouts'),
      '@pages': path.resolve(__dirname, 'pages'),
      '@utils': path.resolve(__dirname, 'utils'),
      '@typings': path.resolve(__dirname, 'typings'),
    },
  }
}

webpack.confing.ts

{
  "compilerOptions": {
   		...
   "paths": {
     "@hooks/*": ["hooks/*"],
     "@components/*": ["components/*"],
     "@layouts/*": ["layouts/*"],
     "@pages/*": ["pages/*"],
     "@utils/*": ["utils/*"],
     "@typings/*": ["typings/*"]
   }
 }
}


🛢 Server

Server 환경은 Express, TypeORM이 사용되며, MySQL와 연동하는 과정만 수행해주면 DB를 구축할 수 있었다.

연동 과정

1️⃣ config/config.json 설정(MYSQL 접속 설정)
 
2️⃣ npx sequelize db:create(스키마 생성) 
 
3️⃣  npm run dev (테이블 생성)

4️⃣ npx sequelize db:seed:all(기초 데이터 넣기)

5️⃣ npm run dev (db 연결 성공)


📡 Route 설정

React 앱은 /와 같이 root 경로에 방문하면 해당 경로에 대한 컴포넌트뿐만 아니라 나머지 컴포넌트까지 자동으로 불러오게 된다. 이는 bundle 사이즈를 증가시키며, 불필요한 동작이다.

따라서, React.lazy 함수를 사용하면 동적 import를 사용해서 컴포넌트를 렌더링 할 수 있다.

즉, lazy 컴포넌트가 있는 컴포넌트가 요청되었을 때 비동기식으로 import해서 필요할 때만 불러오게 설정해줍니다.

또한, lazy 컴포넌트는 Suspense 컴포넌트 하위에서 렌더링되어야 하며, Suspense는 lazy 컴포넌트가 로드되길 기다리는 동안 로딩 화면과 같은 예비 컨텐츠를 보여줄 수 있게 해줍니다.

import LoadSpinner from '@components/LoadSpinner';
import React, { Suspense } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';

const Workspace = React.lazy(() => import('@layouts/Workspace'));
const LogIn = React.lazy(() => import('@pages/LogIn'));
const SignUp = React.lazy(() => import('@pages/SignUp'));

const App = () => (
  <Suspense fallback={<LoadSpinner />}>
    <Routes>
      <Route path="/" element={<Navigate replace to="/login" />} />
      <Route path="/login" element={<LogIn />} />
      <Route path="/signup" element={<SignUp />} />
      <Route path="/workspace/:workspace/*" element={<Workspace />} />
    </Routes>
  </Suspense>
);

export default App;

React.lazy와 Suspense는 서버 사이드 렌더링을 할 수 없다.

따라서, 서버에서 렌더링 된 앱에서 코드 분할을 하려면 Loadable Components를 사용할 수 있다

💡 Code Splitting
👉 React - Code Splitting
👉 React 공식문서
👉 React.lazy와 React.Suspense 완벽 가이드
👉 [리액트] 코드 분할(코드 스플리팅) - React lazy, Suspense, Loadable Components



✚ CORS 설정

다른 포트 설정으로 개발을 진행하는 클라이언트와 서버 간의 CORS 문제는 이제 환경을 설정하며 필수적으로 해결하고 넘어가는 부분이다. 그리고 두가지 방법을 통해 해결할 수 있다.

👉 Proxy

웹팩 데비 서버는 웹 애플리케이션을 개발하는 과정에서 유용하게 쓰이는 도구이다.

그리고 프록시 설정이 가능하며, 이를 통해 클라이언트 측에서 서버측의 API가 필요할때 출처를 속여(위장하여) 통신이 가능하게 한다.(참고)

const config: Configuration = {
  	... 
  devServer: {
    historyApiFallback: true,
    port: 3090,
    devMiddleware: { publicPath: '/dist/' },
    static: { directory: path.resolve(__dirname) },
    proxy: {
      '/api/': {
        target: 'http://localhost:3095',
        changeOrigin: true,
        ws: true,
      },
    },
  },
};



👉 CORS middleware

서버 측에서 Access-Control-Allow-Origin 헤더를 넣어서 이 헤더는 클라이언트 도메인의 요청을 허락하겠다는 설정을 해줄 수 있다.(참고)

특히, 사용자 인증 처리를 위해 쿠키 공유에 대한 허락을 위한 설정도 필수이다.

const express = require('express')
const cors = require('cors');
 
const app = express();

// 이제 응답에 Access-Control-Allow-Origin 헤더가 자동으로 추가
app.use(cors({
    origin: "*", // 모든 출처 허용 옵션. true 를 써도 된다.
    credential: true // 사용자 인증이 필요한 리소스(쿠키 ..등) 접근
}));
profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글