Skip to content

๐Ÿš€ ๋ Œ๋”๋ง ๋ฒ”์ธ์€ ํ•˜๋‚˜!

Kang Chaeryeon edited this page Dec 4, 2024 · 3 revisions

๐Ÿ”ฅย ๋ฌธ์ œ์ƒํ™ฉ

ํ‰ํ™”๋กญ๊ฒŒ ๊ฐœ๋ฐœ์„ ํ•˜๊ณ  ์žˆ๋Š” ์–ด๋Š๋‚ ,,

๋งˆ์Šคํ„ฐํด๋ž˜์Šค์—์„œ ์ฑ„ํŒ…์„ ์ž…๋ ฅํ•˜๋ฉด ์ŠคํŠธ๋ฆฌ๋ฐ ํŽ˜์ด์ง€์— ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚œ๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

React Dev Tools์˜ Highlight updates when components render ์˜ต์…˜์„ ์ฒดํฌํ•˜๊ณ ,

rendering

๋ Œ๋”๋ง ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด ๋ดค์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ฒ”์ธ ์ฐพ๊ธฐ๊ฐ€ ์‹œ์ž‘์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค

๐Ÿš€ย ํ•ด๊ฒฐ ๊ณผ์ •

1. Profiling ํ•˜๊ธฐ

๊ฐ€์žฅ ๋จผ์ € React Dev Tools Profiler๋ฅผ ์—ด์–ด์„œ ์ •ํ™•ํžˆ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ Œ๋”๋ง๋˜๊ณ , ์–ด๋–ค ์ด์œ ๋กœ ๋ Œ๋”๋ง์ด ๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ดค์Šต๋‹ˆ๋‹ค. image

์ŠคํŠธ๋ฆฌ๋ฐ ์ปดํฌ๋„ŒํŠธ์˜ AudioController๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ์•„์ด์ฝ˜๋“ค๊ณผ ์ฑ„ํŒ… ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ๋ Œ๋”๋ง ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ๋˜๋Š” ์š”์†Œ๋“ค์˜ ๋ถ€๋ชจ ์š”์†Œ์—์„œ ๊ฐ ์š”์†Œ๋“ค์ด ์˜์กด์„ฑ์ด ์žˆ๋Š” ๊ฒƒ์ธ๊ฐ€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

2. ์ฝ”๋“œ ๋œฏ์–ด๋ณด๊ธฐ

export function StreamingPage() {
  const { isConnected, connect, reset, userCount } = useSocketStore();
  const { clearMessages } = useChatMessageStore();
  const { roomId } = useParams<{ roomId: string }>();

  useEffect(() => {
		// ์†Œ์ผ“ ์—ฐ๊ฒฐ
  }, [roomId]);

  return (
    <div className="flex flex-row h-screen">
      <div className="flex-grow min-w-[calc(100%-340px)]">
        <NetworkBoundary key={roomId}>
          <StreamingContainer />
        </NetworkBoundary>
      </div>
      <div className="h-screen bg-grayscale-900 flex-shrink-0 w-[340px] text-grayscale-100 px-8 pt-10 pb-10 flex flex-col">
        <div className="flex justify-between items-center mb-4">
          <div className="text-2xl font-bold">์ฑ„ํŒ…</div>
          <div className="flex items-center gap-2">
            <Person />
            <span className="text-lg">{userCount}๋ช…</span>
          </div>
        </div>
        <ChattingContainer />
      </div>
    </div>
  );
}

๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ€๋‹ค ๋ณด๋‹ˆ Page ๋ ˆ์ด์•„์›ƒ ๊นŒ์ง€ ์˜ฌ๋ผ์˜ค๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

StreamingContainer ์—์„œ ์˜ค๋””์˜ค์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ Œ๋”๋ง์ด ๋˜๋ฉด, Person ์•„์ด์ฝ˜๊ณผ ChattingContainer ๊ฐ€ ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํ™•์‹คํ•˜๊ฒŒ ์•Œ๊ธฐ ์œ„ํ•ด์„œ ์ฑ„ํŒ…๊ณผ ๊ด€๋ จ๋œ div ๋ฅผ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด์„œ React.memo ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ์ œ์ด์…˜์„ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

export const Chatting = React.memo(function ChatSection() {
  const userCount = useSocketStore((state) => state.userCount);

  return (
    <div className="h-screen bg-grayscale-900 flex-shrink-0 w-[340px] text-grayscale-100 px-8 pt-10 pb-10 flex flex-col">
      <div className="flex justify-between items-center mb-4">
        <div className="text-2xl font-bold">์ฑ„ํŒ…</div>
        <div className="flex items-center gap-2">
          <Person />
          <span className="text-lg">{userCount}๋ช…</span>
        </div>
      </div>
      <ChattingContainer />
    </div>
  );
});

์ฑ„ํŒ… ๋ถ€๋ถ„ ์ž์ฒด๋ฅผ ๋ฉ”๋ชจ์ œ์ด์…˜ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์žฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ Œ๋”๋ง์€ ๊ณ„์† ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์›์ธ์„ ์ฐพ์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

export function StreamingPage() {
  const { connect, reset } = useSocketStore();
  const { clearMessages } = useChatMessageStore();
  const { roomId } = useParams<{ roomId: string }>();

  useEffect(() => {
		// ์†Œ์ผ“ ์—ฐ๊ฒฐ
  }, [roomId]);

  return (
    <div className="flex flex-row h-screen">
      <div className="flex-grow min-w-[calc(100%-340px)]">
        <NetworkBoundary key={roomId}>
          <StreamingContainer />
        </NetworkBoundary>
      </div>
			<Chatting />
    </div>
  );
}

์ฑ„ํŒ… ์ปดํฌ๋„ŒํŠธ๋งŒ ์–ป์€ ์ฑ„๋กœ ๋‹ค์‹œ ์ŠคํŠธ๋ฆฌ๋ฐ ํŽ˜์ด์ง€๋กœ ๋Œ์•„์™”์Šต๋‹ˆ๋‹ค.

์ŠคํŠธ๋ฆฌ๋ฐ ํŽ˜์ด์ง€์˜ ์ƒํƒœ ๋ณ€์ˆ˜๋“ค์„ ํ™•์ธํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

๊ต‰์žฅํžˆ ์ˆ˜์ƒํ•ด๋ณด์ด๋Š” ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ๋Š” ์†Œ์ผ“ ํ•จ์ˆ˜๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์†Œ์ผ“์— ๊ด€ํ•œ ๋‚ด์šฉ์€ zustand๋ฅผ ์ด์šฉํ•ด useSocketStore ๋กœ ์ „์—ญ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ๋“ zustand ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์ƒํƒœ๋“ค์€ ๋ Œ๋”๋ง์ด ๋‹ค์‹œ ๋  ๊ฒƒ์ด๋‹ค ๋ผ๋Š” ์ƒ๊ฐ์œผ๋กœ ํ™•์ธํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

3. zustand ๋ Œ๋”๋ง ์ตœ์ ํ™”

zustand์—์„œ ์ „์ฒด ์Šคํ† ์–ด๋ฅผ ๊ตฌ๋…ํ•˜๊ฒŒ ๋˜๋ฉด, ์Šคํ† ์–ด์— ๋‹ด๊ณ ์žˆ๋Š” ๋ชจ๋“  state๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๊ตฌ๋…์ค‘์ธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ Œ๋”๋ง ๋œ๋‹ค

๋”ฐ๋ผ ํŠน์ • state๋งŒ ๊ตฌ๋…ํ•ด ์„ ํƒ์  ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 // ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง ๋ฐœ์ƒ
 const { clearMessages } = useChatMessageStore();
 
 // ํ•„์š”ํ•œ state๋งŒ ๊ตฌ๋…
 const clearMessages = useChatMessageStore((state) => state.clearMessages);

ํ•„์š”ํ•œ state๋งŒ ๊ตฌ๋…์„ ํ•˜๋˜ ์ค‘์— 2๊ฐœ๋ฅผ ์š”์ฒญํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

image

ํ•ด๋‹น ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์›์ธ์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋Œ€์‹  ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ, ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ์ƒํƒœ๊ฐ€ ๊ณ„์† ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๊ฒฝ์šฐ, useEffect๋‚˜ ์ƒํƒœ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๊ฐ€ ์„œ๋กœ๋ฅผ ๋ฌดํ•œํžˆ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ€๊ฒฝํ•œ useSocektStore ์ฐธ์กฐ ํ•จ์ˆ˜์—์„œ zustand selector ํ•จ์ˆ˜๊ฐ€ ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฌดํ•œ ์—…๋ฐ์ดํŠธ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ํ•ด๋‹น ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ๊ฐ ๋”ฐ๋กœ selector๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ฒ ์ง€๋งŒ, zustand์˜ shallow๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

const { connect, reset } = useSocketStore(
  useShallow((state) => ({ connect: state.connect, reset: state.reset })),
);

4. ๊ฒฐ๊ณผ

image

image

์ฑ„ํŒ… ์ปดํฌ๋„ŒํŠธ๋Š” ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š” ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค!!

๐Ÿคธโ€โ™€๏ธย ํ›„๊ธฐ

  • zustand์˜ shallow๊ฐ€ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ์„ ๋ Œ๋”๋ง์„ ์‹œํ‚จ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • React Dev Tools์˜ Profiler ์‚ฌ์šฉ์ด ๋Šฅ์ˆ™ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
  • ์„ค๊ณ„๋ฅผ ๋” ๊ผผ๊ผผํžˆ ํ–ˆ๋‹ค๋ฉด ์›์ธ ํŒŒ์•…์ด ๋” ๋นจ๋ž์„ ๊ฒƒ ๊ฐ™์•„, ์ปดํฌ๋„ŒํŠธ ์„ค๊ณ„์˜ ์ค‘์š”์„ฑ์„ ๋Š๋ผ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

inear

๊ธฐ์ˆ  ๊ณต์œ 

๐Ÿš€ ffmpeg๋Š” stderr๋กœ ๋””๋ฒ„๊น…์„ ํ•˜๋Š” ์ด์œ 
๐Ÿš€ HLS ํ”„๋กœํ† ์ฝœ์— ๊ด€ํ•œ ์ •๋ฆฌ ๋ฐ FFmpeg ์‚ฌ์šฉ๊ธฐ
๐Ÿš€ ๋น„ํŠธ๋Š” tsconfig.json์ด ์„ธ ๊ฐœ?
๐Ÿš€ NestJS ๊ธฐ๋ณธ ๊ฐœ๋… - Modules
๐Ÿš€ Socket.io ์ตœ(๊ฐ•)์ ํ™”
๐Ÿš€ ๋„์ปค์™€ nginx์˜ ์‚ฌ์šฉ๊ธฐ
๐Ÿš€ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์ž

๊ฐœ๋ฐœ ์ผ์ง€

๐Ÿš€ FSD ์‚ฌ์šฉ๊ธฐ, ๊ทผ๋ฐ ์ด์ œ ๋‚˜๋งŒ์˜ ๊ทœ์น™์„ ๊ณ๋“ค์ธ
๐Ÿš€ CICD ๊ตฌ์กฐ ์ˆ˜์ •
๐Ÿš€ ์•จ๋ฒ” ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ํ•˜๊ธฐ (with HLS)
๐Ÿš€ HLS๋กœ ์Œ์•… ์ฃผ๊ณ ๋ฐ›๊ธฐ
๐Ÿš€ vite + react + typescript ํ™˜๊ฒฝ์—์„œ path alias ์„ค์ •
๐Ÿš€ React Scan์ด ๋ญ์ฃ ?
๐Ÿš€ ๋กœ์ปฌ ํ™˜๊ฒฝ ๊ฐœ๋ฐœ ๋ชจ๋“œ ๋ฐฐํฌ
๐Ÿš€ ์•จ๋ฒ” ์ „์ฒด๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐํ•œ๋‹ค๊ณ ? (with HLS)
๐Ÿš€ ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž

ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

๐Ÿš€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ HLS ERROR
๐Ÿš€ input ํƒœ๊ทธ์— ํ•œ๊ธ€ ์ž…๋ ฅ ํ›„, Enter๋ฅผ ๋ˆ„๋ฅด๋ฉด ํ•จ์ˆ˜๊ฐ€ ๋‘๋ฒˆ ํ˜ธ์ถœ๋˜๋Š” ์˜ค๋ฅ˜
๐Ÿš€ nginx proxy pass๋ฅผ ๋ฐ”๊ฟจ๋”๋‹ˆ ์ƒ๊ธด ์—๋Ÿฌ - ์Šค์›จ๊ฑฐ ์ธ์‹ ๋ฌธ์ œ
๐Ÿš€ ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ WS handshake
๐Ÿš€ ๋ Œ๋”๋ง ๋ฒ”์ธ์€ ํ•˜๋‚˜!

ํ˜‘์—… ๊ทœ์น™

๐ŸŒˆ ๊ทธ๋ผ์šด๋“œ ๋ฃฐ
๐Ÿฅ” ํŒ€์› ์†Œ๊ฐœ
๐Ÿ”Ž ์ฝ”๋“œ & ๊นƒ ์ปจ๋ฒค์…˜
๐ŸŒณ ๊นƒ branch ์ „๋žต
๐Ÿ“Œ ๋…ธ์…˜ ๋ฌธ์„œ ์ €์žฅ์†Œ

ํ”„๋กœ์ ํŠธ ๊ธฐํš

๐ŸŽจ ํ”ผ๊ทธ๋งˆ
๐Ÿง‘โ€๐Ÿ’ป ๊ธฐํš ๊ณต์œ  ๋ฐœํ‘œ ์ž๋ฃŒ
๐ŸŽค 2์ฃผ์ฐจ ๋ฐœํ‘œ ์ž๋ฃŒ
๐Ÿ˜Ž ๋ฐฑ๋กœ๊ทธ

๋ฐ์ผ๋ฆฌ ์Šคํฌ๋Ÿผ

๐Ÿ“ 1์ฃผ์ฐจ
๐Ÿ“ 2์ฃผ์ฐจ
๐Ÿ“ 3์ฃผ์ฐจ
๐Ÿ“ 4์ฃผ์ฐจ
๐Ÿ“ 5์ฃผ์ฐจ

์ฃผ๊ฐ„ ๊ณ„ํš์„œ

๐Ÿ—“๏ธ 1์ฃผ์ฐจ
๐Ÿ—“๏ธ 2์ฃผ์ฐจ
๐Ÿ—“๏ธ 3์ฃผ์ฐจ
๐Ÿ—“๏ธ 4์ฃผ์ฐจ
๐Ÿ—“๏ธ 5์ฃผ์ฐจ

๊ทธ๋ฃน ํšŒ๊ณ 

โœจ 1์ฃผ์ฐจ
โœจ 2์ฃผ์ฐจ
โœจ 3์ฃผ์ฐจ
โœจ 4์ฃผ์ฐจ


view

Clone this wiki locally