๐งโ๐ซ 4์ฃผ์ฐจ ๋ฉํ ๋ง - boostcampwm2023/web09-MagicConch GitHub Wiki
๋ฉํ ๋ง
โ๏ธย ์์ ๋ค ๋ฐ ์ง๋ฌธ
๋ฉํ ๋ง ์์ ๋ค์ ๋ฉํ ์๊ฒ ํ๊ณ ์ถ์ ์ง๋ฌธ์ ์ฌ์ ์ ์ค๋นํฉ๋๋ค.
FE 1๏ธโฃย ํ ์ด ์ปค์ง์๋ก ๊ด๋ฆฌํ๊ธฐ๊ฐ ์ ์ ํ๋ค์ด์ง๋๋ค.
ํ์ฌ useWebRTC ์์ชฝ์ ์ด๋ค ํ ์ด ๋ง๋ ์ํ๊ฐ ๋ค๋ฅธ ํ ์์ ์ฌ์ฉ๋๊ธฐ๋ ํ๊ณ , ํ ๋ฐ์ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ฌ์ฉ๋๊ธฐ๋ ํฉ๋๋ค. ์ด ๋ ์ํ๋ฅผ ์ ์ญ์ผ๋ก ๋นผ๋๊ฒ ๋ง์๊น์? ์ ์ญ์ผ๋ก ๋นผ๋ฉด ์์กดํ๋ ์ํ๋ฅผ ์์๋ณด๊ธฐ ๋ ์ด๋ ค์ง๊ฒ๊ฐ์ ๊ณ ๋ฏผ๋ฉ๋๋ค.
import { useEffect } from 'react';
import { useControllMedia } from './useControllMedia';
import { useDataChannel } from './useDataChannel';
import { useMedia } from './useMedia';
import { useMediaInfoContext } from './useMediaInfoContext';
import { useRTCPeerConnection } from './useRTCPeerConnection';
import { useSignalingSocket } from './useSignalingSocket';
import { useSocket } from './useSocket';
export function useWebRTC(roomName: string) {
const { socketEmit } = useSocket('WebRTC');
const { mediaInfos } = useMediaInfoContext();
const { localVideoRef,
remoteVideoRef,
localStreamRef,
cameraOptions,
audioOptions,
getMedia,
getAudiosOptions,
getCamerasOptions,
} = useMedia();
const { peerConnectionRef, makeRTCPeerConnection, closeRTCPeerConnection } = useRTCPeerConnection({
roomName,
remoteVideoRef,
});
const { mediaInfoChannel, chatChannel, initDataChannels, closeDataChannels } = useDataChannel({
peerConnectionRef,
});
const { addTracks, changeMyAudioTrack, changeMyVideoTrack, toggleAudio, toggleVideo } = useControllMedia({
localStreamRef,
peerConnectionRef,
localVideoRef,
mediaInfoChannel,
getMedia,
});
const negotiationDataChannels = () => {
closeRTCPeerConnection();
closeDataChannels();
makeRTCPeerConnection();
initDataChannels();
addTracks();
};
const { initSignalingSocket } = useSignalingSocket({
roomName,
peerConnectionRef,
negotiationDataChannels,
});
useEffect(() => {
const initOnMount = async () => {
await getMedia({});
initSignalingSocket();
makeRTCPeerConnection();
initDataChannels();
addTracks();
socketEmit('joinRoom', roomName);
};
initOnMount();
return () => {
closeRTCPeerConnection();
closeDataChannels();
};
}, []);
return {
cameraOptions,
audioOptions,
localVideoRef,
remoteVideoRef,
mediaInfoChannel,
chatChannel,
mediaInfos,
toggleAudio,
toggleVideo,
addTracks,
changeMyAudioTrack,
changeMyVideoTrack,
getAudiosOptions,
getCamerasOptions,
getMedia,
};
}
๐ก ๋ต๋ณ ) hook์์ ๋๋ฌด ๋ง์ ๊ฒ๋ค์ ์ฒ๋ฆฌํ๋ค. ๋ชจ๋ํํด์ ์ฌ์ฉํ๋ ๊ฑด ์ด๋จ๊น?
- ํด๋น ๋ถ๋ถ์ ๋ชจ๋๋ก ๋นผ์ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ๋ฉด ์ข์ ๋ฏํ๋ค.
- ํ ์ ์์กด๊ด๊ณ๊ฐ ๋๋ฌด ๋์์ง๋ฉด ์ถ์ ํ๊ธฐ๊ฐ ์ด๋ ต๋ค.
- ๋น์ฆ๋์ค ๋ก์ง์ ๋ฐ๋ผ ์ด๋ฅผ ๋ถ๋ฆฌํด๋ณด์!
FE 2๏ธโฃ socket.io์์ join
๐ก ๋ต๋ณ ) hoxy ์ฌ์ฐ๊ฒฐ ์ด์..?
BE 1๏ธโฃ ์๊ทธ๋๋ง ์๋ฒ๊ฐ ์น์์ผ ์์ฒญ์ ๋ฐ์ง ๋ชปํ๊ณ ์์ต๋๋ค ๐
ํ์ฌ ์ด 3๊ฐ์ ๋์ปค ์ปจํ ์ด๋๋ฅผ ๋์ฐ๊ณ ์์ต๋๋ค.
- nginx
- was 3000
- signal 3001
nginx์์ was์ signal ์๋ฒ๋ก ํ๋ก์ ํ๋๋ฐ was๋ก http ์์ฒญ์ ํ๋ก์ ํ๋ ๊ฒ์ ๋์ง๋ง, signal ์๋ฒ๋ก ์น์์ผ ์์ฒญ์ ํ๋ก์ ํ๋ ๊ฒ์ด ๋์ํ์ง ์์ต๋๋ค.
์ด๊ฑด nginx ํ์ผ์
๋๋ค. ์น์์ผ ์์ฒญ์ด๋ฏ๋ก upgrade ๋ฅผ ๋ฃ์ด์ฃผ์์ต๋๋ค.
- nginx ์ค์ ํ์ผ
server {
listen 80;
server_name was.tarotmilktea.com;
...
location /signal { # signal ์๋ฒ
proxy_pass http://signal:3001;
proxy_redirect default;
proxy_http_version 1.1; # ๋ฒ์ ๋ช
์
proxy_set_header Upgrade $http_upgrade; # upgrade ์ค์
proxy_set_header Connection "upgrade"; # upgrade ์ค์
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://was:3000;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
access_log /var/log/nginx/nest-app-access.log;
error_log /var/log/nginx/nest-app-error.log;
}
server {
listen 443 ssl;
server_name was.tarotmilktea.com;
...
location /signal { # signal ์๋ฒ
proxy_pass http://signal:3001;
proxy_redirect default;
proxy_http_version 1.1; # ๋ฒ์ ๋ช
์
proxy_set_header Upgrade $http_upgrade; # upgrade ์ค์
proxy_set_header Connection "upgrade"; # upgrade ์ค์
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://was:3000;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
access_log /var/log/nginx/nest-app-access.log;
error_log /var/log/nginx/nest-app-error.log;
}
docker ps ๊ฒฐ๊ณผ์
๋๋ค. 3๊ฐ์ ์ปจํ
์ด๋๊ฐ ๋ชจ๋ ์ ๋ ์์ต๋๋ค.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
447a1f84b6bb app_nginx "/docker-entrypoint.โฆ" 43 minutes ago Up 43 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp app_nginx_1
77870bb6c78c app_signal "docker-entrypoint.sโฆ" 55 minutes ago Up 55 minutes 3001/tcp app_signal_1
3a5e3e02a3a8 app_was "docker-entrypoint.sโฆ" About an hour ago Up About an hour 3000/tcp
nginx ์ปจํ ์ด๋์ ๋ค์ด๊ฐ์ http ๋ฐ wss ์์ฒญ์ ๋ณด๋ธ ๊ฒฐ๊ณผ์ ๋๋ค.
# http ์์ฒญ (GET / ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์ปจํธ๋กค๋ฌ๊ฐ ์์ด์ Not Found๊ฐ ๋ด์ต๋๋ค)
root@447a1f84b6bb:/$ curl http://signal:3001
{"message":"Cannot GET /","error":"Not Found","statusCode":404}
# wss ์์ฒญ (curl๋ก wss ์์ฒญ ๋ณด๋ด๊ธฐ??)
root@447a1f84b6bb:/$ curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: was.tarotmilktea.com" http://signal:3001
curl: (52) Empty reply from server
๐ก ๋ต๋ณ ) ๋๋๊ณ ๋ด ์๋ค(?)
์งํ์ํฉ
์ฑํ ๋ฐฉ ๊ฐ์คํ๊ธฐ๋ฅผ ๋๋ ์ ๋ ์์๋๋ ์ฌ์ฉ์ ๊ฐ ์ค์๊ฐ ํ์ ์ฑํ ์์ ํ์ ๊ธฐ๋ฅ๋ค ์๋ฃ- ๋๋ค์/ํ๋กํ ์ค์ ๋ฑ ์ธ๋ถ ๊ธฐ๋ฅ๋ง ๋จ์
- ๊ฐ๋ฐ ๋ฐ ์ ์ง๋ณด์๋ฅผ ์ํด winston ๋ก๊ฑฐ ์ ์ฉํ์ฌ ๋ ๋ฒจ๋ณ๋ก ๋ก๊ทธ ๋จ๊ธฐ๊ธฐ
- ์ถํ
volume์ ํ์ฉํ์ฌ ํธ์คํธ ์๋ฒ์ ๋ก๊ทธ ๋จ๋๋ก ์์ ์์
- ์ถํ
- signal ์๋ฒ express์์ nest๋ก ๋ง์ด๊ทธ๋ ์ด์
๋ค์์ฃผ ๊ณํ
- BE : ๋ฌด์ค๋จ ๋ฐฐํฌ (๋ธ๋ฃจ/๊ทธ๋ฆฐ ์ ๋ต)
- FE : ์ค์๊ฐ ํ์ ์ฑํ ์ธ๋ถ ๊ธฐ๋ฅ ๋ง๋ฌด๋ฆฌ
- ๋ฒ๊ทธ ์ก๊ธฐ
- Object Storage ์ด๋ฏธ์ง ๋ค์ด๋ก๋
- FE : SEO + ๋ค์ด๋ฒ/๊ตฌ๊ธ์ ์ฌ์ดํธ ๋ฑ๋กํ๊ธฐ
- ์ฝ๋ ๋ฆฌํฉํ ๋ง
- ์๋ก ๋ง๋ ๋ฒํผ์ผ๋ก ๊ต์ฒด
- ๋น์ทํ ๊ฒ๋ผ๋ฆฌ ํด๋ ์ ๋ฆฌ
- ์ํ ๊ด๋ฆฌ ๊ด๋ จ
- ํฉํ ๋ฆฌ ํ์ฉ
- ๋ฉํ ๋ง ํผ๋๋ฐฑ ๋ฐ์
- ์๋ฌ ํธ๋ค๋ง
- FE : ์ฑ๋ฅ ์ต์ ํ (๋ ๋๋ง ์ต์ ํ & ๋ก๋ฉ์๋ ์ต์ ํ) โ react developer tools & lighthouse ํ์ฉํ๊ธฐ
- ํ ์คํธ ์ฝ๋
- BE : ๋ก๊ทธ ๊ด๋ฆฌ
- FE : ์น ์ ๊ทผ์ฑ (์๋ง 6์ฃผ์ฐจ)
- ๋ฌธ์ํ ๐ฅบ (์๋ง 6์ฃผ์ฐจ)
โ๏ธย ๋ฉํ ๋ง ๋ด์ฉ
๋ฉํ ๋ง ์๊ฐ์ ๋๋ ์ด์ผ๊ธฐ๋ฅผ ๊ธฐ๋กํด๋ณด์ธ์.
๐ ๊ณตํต ์ฝ๋ฉํธ
- ์ปค๋ฐ์ ์ด์ ๋ฅผ ๋ฌ์
- ๊ธฐ์ ์ ๋์ ๊ธ๋ก ๊ณต์ ํด๋ณด๋ ๊ฒ ์ด๋จ๊น?
- ํจ์์ ์ด๋ฆ์ ๋ณด๊ณ ์์์ด ๋๋ฉด ์ข๊ฒ ๋ค.
๐ FE ์ฝ๋ฉํธ
- hook์ผ๋ก๋ง ์ฒ๋ฆฌํ๋ ค๊ณ ํ์ง๋ง๊ณ ๋ชจ๋ํ๋ฅผ ํ์
- ํ ์ ๋์์๋ฆฌ๋ฅผ ์ ๋๋ก ์ดํดํ์ง ๋ชปํ๋ฉด ๋์ค์ ๊ผฌ์ผ ์ ์์
- ์ง๋ฌธ ) ๋ฐ์ดํฐ ์ฑ๋๋ก ์ฑํ
๊ตฌํํ๋๋ฐ ์ด๋ค ์ ์ด ์ข์๋?
- ๋ต๋ณ ) ์์ผ๊ณผ ๋ณ ์ฐจ์ด๊ฐ ์์๋ค.
- ๋ฐ์ดํฐ ์ฑ๋์ ํ์ผ๋ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค๋ ์ฅ์ ์กด์ฌ
- ๋ฐ์ดํฐ ์ฑ๋์ ์ปค๋ฅ์ ์ ๋งบ์๋๋ง๋ค ๋ชจ๋ ๋ค์ ํ์์ ์งํํด์ผ ํ๋๊ฒ ๋ง๋ค.
- ์ด๋ค ํ
์ธ์ง ๋๋ฉ์ธ ๋ณ๋ก ๊ตฌ๋ถ์ ํด๋ณด๋๊ฒ ์ข๊ฒ ๋ค!
- ์ปดํฌ๋ํธ์์ controlled , uncontrolled ์ปดํฌ๋ํธ๊ฐ ์๋ค.
- matariel ui ๋ฑ์ ์ด๋ค๋ฉด ๋ฉํ์ด ๋ง์ด ๋ ์๊ฐ ์๊ณ ๊น์ด๊ฐ ๊น์ด์ง๋ฉด ์ด๋ค ์ ๊ฐ ์ํ๋ฅผ ๋ฐ๊พธ๋์ง ๋ชจ๋ฅธ๋ค.
- ์ ๋ช ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ด ๋ ๊ฐ์ ๋ํ ๊ธฐ์ค์ ์๊ฐํด๋ณด๋ฉด ๋์์ด ๋ ๊ฒ ๊ฐ๋ค.
- ํ โ Chat์ผ๋ก ๊ฐ๋ ๋ถ๋ถ ์์ผ ์ฐ๊ฒฐ ๊ณ ๋ ค
- ํ์ฌ ์์ผ ์ฐ๊ฒฐ์ ๋งบ๊ณ ๊ฐ๊ณ ์์
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ ์ ์ธ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์๋ ์๊ณ ๊ฐ์ ธ์ค๋ ๋ก์ง์ ํธํ๊ฒ ๋ถ๋ฆฌ ๊ฐ๋ฅ! ์ฌ์๋ ๋ก์ง๋ ํธํ๊ฒ ์งค ์ ์๋ค. ์คํ๋ผ์ธ์ผ ๋ ์บ์๋ก ๋ค์ดํฐ๋ ์ฑ์ฒ๋ผ ์๋ํ๊ฒ ํ ์ ์๋ค!
- ์์ฌ์ด ์
- ์ํ๋ฒณ์ ์ ๋ ฌ์ด๋ผ ์ด๋ฆ๋ง ๊ฐ์ง๊ณ ์ถ๋ก ํ๊ธฐ ํ๋ฌ โ ๋๋ฉ์ธ๋ณ๋ก ๋๋์?
- ์ต๋ํ useref ๋ฑ์ ์ฌ์ฉํ uncontrolled ๋ฐฉ์์ผ๋ก ํ ์ ๊ฐ์ ํด์ ์ฌ์ด๋์ดํํธ๋ฅผ ์ค์ด๋ ๊ฑธ ์ถ์ฒ
- ํจ์์ ์ด๋ฆ์ ๋ณด๊ณ ๋์์ด ์์์ด ๋์ด์ผํ๋๋ฐ ์์์ด ์๋๋ ๋ถ๋ถ์ด ๊ฐํน ๋ณด์
- useSocket
- ํ์์ ์ฑํ
์ผ๋ก ๊ฐ ๋ ์์ผ์ ์ฐ๊ฒฐํ๊ณ ๋์ด๊ฐ๋ ๋ฌธ์ โ ์์ ์ถ์ฒ
- ํ์ด์ง์์ ์๋ก๊ณ ์นจํ๋ฉด ์์ผ ์ฐ๊ฒฐ Init X
- useQuery vs useSuspendQuery
๐ BE ์ฝ๋ฉํธ
- ์์ธ ์ฒ๋ฆฌ ๊น์ด๊ฐ ์ข ๊น๋ค.
- catch๋ฅผ ์ฌ๋ฌ ๋ฒ ํด๋ ๊ด์ฐฎ๋ค.
- early return๋!
- ๋ฏธ๋ค์จ์ด๋ก exception์ ๋ฐ์์ ์ฒ๋ฆฌ
- ๋ฏธ๋ค์จ์ด๋ก ๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณด์.
- clova ๊ด๋ จ ์์ ์ค์ ๋ฐฐ์ด์ด ์๋ค.
- ๋ฐฐ์ด์ ์์ ์ด ๊ฐ๋ฅํ๋ค. ์ ๋ง ์์๋ก ๋ง๋ค์ด๋ณด๋ ๊ฒ ์ด๋จ๊น?
- ํฉํ ๋ฆฌ๋ฅผ ํ์ฉํด๋ณด์
- DTO, entity ์์ฑํ ๋
- URL ์์ฑํ ๋
null์ ์๋ฏธ๋ฅผ ๋ถ์ฌํด๋ณด์ (undefinedvs.null)- ์์ผ๋ฉด exception ์ฒ๋ฆฌ
๐ ๊ธฐํ
- 17์กฐ ์ฑํ
๊ด๋ จ ์ฝ๋ฉํธ : ๋ฉํ ๋์ ๊ฒฝ์ฐ, http2์
keep alive๋ก ์ค์๊ฐ ์ฑํ ๊ตฌํํ๋ค. - FE์ BE ๊ณตํต ๋ชจ๋ ๊ด๋ฆฌ โ 17์กฐ : turbo์ internal package
package.json์main,types์์๋ณด๊ธฐ- eslint ๋์ ์ฐพ์๋ณด๊ธฐ (ex. ๋ค์์คํ์ด์ค ์ด๋ป๊ฒ ์ฐพ๋์ง)
- https://toss.tech/article/commonjs-esm-exports-field
- ํ์ง๋ง ์๊ฐ ๋ง์ด ์ก์๋จน์ผ๋ ๋์ค์ผ๋ก ๋ฏธ๋ฃฐ ๊ฒ
- mock : https://github.com/google/intermock
- graphQL โ ๋ฐฑ/ํ๋ก ํธ api ๋ถ์์ ์ด์์ ์ผ๋ก ํ์ด๋ผ ์ ์์!
- ngrok, cloud tunnel ๋ด ๋ก์ปฌ์ ์๋ฒ์ฒ๋ผ ๋์ธ์์๋ค. ํด๋ผ์ฐ๋ํ๋ ์ด ํฐ๋ โ pc ip๊ฐ ํ๋ก์๊ฐ ๋ผ์ ์ธ๋ถ์์ ์ ์์ด ๊ฐ๋ฅํ๋ค. โ ๋ด๊ฐ ๊ฐ์ง๊ณ ์๋ ๋๋ฉ์ธ๋ ๋ถ์ผ ์ ์๋ค.
โ๏ธย ์ฒดํฌ๋ฆฌ์คํธ
์ด๋ฒ ์ฃผ ๋ฉํ ๋ง์์ ์ด์ผ๊ธฐ ๋๋๋ฉด ์ข์ ์ฃผ์ ์ ๋๋ค. ์ฐ๋ฆฌ ํ์ ์ํฉ์ ์ด๋ค๊ฐ์? ๋ฉํ ๋๊ณผ ์ด์ผ๊ธฐ ๋๋ ํ ์ ํ ์ฒดํฌํ๊ณ , ๊ทธ ์ด์ ๋ฅผ ์์ฑํด๋ณด์ธ์. ์ถ๊ฐํ๊ณ ์ถ์ ํญ๋ชฉ์ด ์๋ค๋ฉด ์ง์ ์ถ๊ฐํด๋ ์ข์ต๋๋ค.
- ๊ธฐ์ ์ ๋์ ๊ณผ์ ์๋ฆฝ์ด ๋์๋ค.
- ํ์ค์ฑ ์๋ ๊ณํ ์๋ฆฝ์ด ๋์๋ค.
- ํ๋ก์ ํธ ๊ธฐํ๊ณผ ์ค๊ณ์ ๋ผ๋๊ฐ ๋์๋ค.