React TypeScript | ๐Ÿ’ฌ Slack ํด๋ก  ์ฝ”๋”ฉ (4)

rimmzยท2022๋…„ 7์›” 4์ผ
0
post-thumbnail

Slack ํด๋ก  ์ฝ”๋”ฉ[์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… with React]


Slack(์Šฌ๋ž™) ์„œ๋น„์Šค๋ฅผ ๋”ฐ๋ผ ๋งŒ๋“ค๋Š” Reacr Web ๊ฐœ๋ฐœ
โœ… ํ•ด๋‹น ๊ฐ•์˜๋ฅผ ์ˆ˜๊ฐ• ํ›„ ์ž‘์„ฑํ•œ ๋ณต์Šต ๋ฐ ๊ฐœ์ธ ์Šคํ„ฐ๋”” ๊ธฐ๋ก์ž…๋‹ˆ๋‹ค.

๐Ÿšฉ ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… + ๊ฐ์ข… ํ”„๋ก ํŠธ ๊ธฐ์ˆ  ๋ฐฐ์šฐ๊ธฐ

๐Ÿ”˜ socket.io ์ „์šฉ ํ›…์Šค ๋งŒ๋“ค๊ธฐ

Socket.io ? ๐Ÿค”
Websocket์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ค์‹œ๊ฐ„ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
์›น ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์˜ ์‹ค์‹œ๊ฐ„ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” Node.js์˜ ๋ชจ๋“ˆ์ด๋‹ค.

Websocket ? ๐Ÿค”
์›น์†Œ์ผ“์€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ˆ„๋ฝ๋˜์ง€ ์•Š๊ฒŒํ•˜๋Š” tcp๊ธฐ๋ฐ˜์˜ ์–‘๋ฐฉํ–ฅํ†ต์‹ ์„ ์ œ๊ณตํ•˜๋Š” ์ปดํ“จํ„ฐ ํ”„๋กœํ† ์ปฌ

  • npm i socket.io-client@2 socket.io ์„ค์น˜

  • backUrl Url์— ๊ด€๋ จํ•ด์„œ ๋”ฐ๋กœ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•ด ๋†“์•„์•ผ ์ˆ˜์ •์ด ์šฉ์ดํ•˜๋‹ค.

  • [workspace] workspace๋ฅผ namespace ๋กœ ์ง€์ •ํ•˜์—ฌ ํ†ต์‹ 
  • disconnect ํด๋ผ์ด์–ธํŠธ์™€์˜ ์—ฐ๊ฒฐ์ด ๋Š์–ด์กŒ์„ ๋•Œ ๋ฐœ์ƒ

๐Ÿ”˜ socket.io ์ „์šฉ ํ›…์Šค ๋งŒ๋“ค๊ธฐ

  • ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฐ๊ฒฐ๋˜๋ฉด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ณต์œ ๊ฐ€ ๊ฐ€๋Šฅํ•œ๋ฐ,ย socket์„ ๊ทธ๋ƒฅ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ๋“  socket์œผ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.

  • ์Šฌ๋ž™ - ํ•ด๋‹น ์›Œํฌ์ŠคํŽ˜์ด์Šค์—๋งŒ ํ•ด๋‹นํ•˜๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ›์•„์•ผ ํ•˜๋ฏ€๋กœ Namespace ์‚ฌ์šฉํ•ด ํ†ต์‹ ์„ ํ•œ๋‹ค.

Namespace ? ๐Ÿค”
Express์˜ ๋ผ์šฐํŒ…์ฒ˜๋Ÿผ url์— ์ง€์ •๋œ ์œ„์น˜์— ๋”ฐ๋ผ ์‹ ํ˜ธ์˜ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ํ•˜๋Š” ๊ธฐ์ˆ 
๋ง ๊ทธ๋Œ€๋กœ ์ด๋ฆ„์ด ๋ถ™์€ ๊ณต๊ฐ„์ด๋ฉฐ, ์†Œ์ผ“์„ ๋ฌถ์–ด์ฃผ๋Š” ๋‹จ์œ„๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

  • hook์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด์ง„ socket์„ import ํ•˜์—ฌ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์— ์‚ฌ์šฉํ•œ๋‹ค.

  • !sockets[workspace] ๊ธฐ์กด์˜ ์—ฐ๊ฒฐ ๋ฐ์ดํ„ฐ ๊ฐ’์ด ์žˆ์„ ๊ฒฝ์šฐ ๊ธฐ์กด ๋ฐ์ดํ„ฐ return ํ•˜๋„๋ก ํ•œ๋‹ค.

  • transports websocket์œผ๋กœ ์„ค์ •ํ•˜๋ฉด polling ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค.

  • sid socket id

  • ์•ž์— ์ˆซ์ž๋Š” ping-pong์œผ๋กœ ์—ฐ๊ฒฐ์ด ์ž˜ ์œ ์ง€๋˜๊ณ  ์žˆ๋‚˜ socket.io๊ฐ€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

  • onlineList๋ฅผ ํ†ตํ•ด ์ ‘์†ํ•ด์žˆ๋Š” ์œ ์ € ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ”˜ ์ปค์Šคํ…€ ์Šคํฌ๋กค๋ฐ”์™€ dayjs

  • npm install react-custom-scrollbars react-custom-scrollbars ์„ค์น˜

  • autoHide ์Šคํฌ๋กค ๊ธฐ๋Šฅ์„ ์•ˆํ•  ์‹œ ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง€๊ฒŒํ•จ

  • npm install dayjs dayjs ์„ค์น˜ (๋‚ ์งœ ํ˜•์‹ ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
  • format์œผ๋กœ ์›ํ•˜๋Š” ํ˜•์‹์˜ ๋‚ ์งœ ํ‘œ๊ธฐ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

๐Ÿ”˜ ๋ฉ˜์…˜ ๊ธฐ๋Šฅ ๋งŒ๋“ค๊ธฐ

  • npm install react-mentions react-mentions ์„ค์น˜


  • trigger : ๋ฉ˜์…˜ ํ™œ์„ฑํ™” ํŠธ๋ฆฌ๊ฑฐ
  • appendSpaceOnAdd ์ปค์„œ ๋‹ค์Œ์— ํ•œ์นธ ์—ฌ๋ฐฑ ๋„์–ด์ฃผ๋Š” ๊ธฐ๋Šฅ
  • renderSuggestion : ๋ฉ˜์…˜ ํ›„ ๋„์–ด์ ธ ์žˆ๋Š” ์ฐฝ์˜ ๋ Œ๋”๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ธฐ๋Šฅ
  • ๊ณต์‹ ๋ฌธ์„œ์— ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜ ๋“ฑ ํƒ€์ž…์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ž‘์—…ํ•œ๋‹ค.

๐Ÿ”˜ ์ •๊ทœํ‘œํ˜„์‹์œผ๋กœ ๋ฌธ์ž์—ด ๋ณ€ํ™˜ํ•˜๊ธฐ

  • ๋ฉ˜์…˜์œผ๋กœ ์ž…๋ ฅํ•˜์—ฌ ๋œฌ ID๋ฅผ style์„ ์ง€์ •ํ•˜์—ฌ ๊ตฌ๋ถ„ํ•œ๋‹ค.

  • npm i regexify-string regexify-string ์„ค์น˜ (์ •๊ทœํ‘œํ˜„์‹ ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)

  • pattern: /@[(.+?)]((\d+?))|\n/g
  • \d ์ˆซ์ž + 1๊ฐœ ์ด์ƒ
  • ? 0 ๊ฐœ ์ด์ƒ
  • g๋Š” ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  ํ‘œํ˜„์‹ ์ฐพ๊ธฐ

๐Ÿ”˜ ๋‚ ์งœ๋ณ„๋กœ ๋ฌถ์–ด์ฃผ๊ธฐ(position: sticky)

  • makeSection ๊ณตํ†ต ํ•จ์ˆ˜ ์ž‘์—…
  • monthDate ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ์—์„œ ๋‚ ์งœ๋ฅผ format('YYYY-MM-DD') ํ˜•์‹์œผ๋กœ ์ถ”์ถœํ•œ๋‹ค.

  • reverse() ์ตœ์‹  ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋ž˜๋กœ ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๋ฐฐ์—ด์˜ ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.
  • ๋ฐ์ดํ„ฐ ๋ถˆ๋ณ€์„ฑ์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด
  • [].concat(...chatData) : ๊ธฐ์กด ๋ฐฐ์—ด๊ณผ ๋™์ผํ•œ ๋ฐฐ์—ด๋กœ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด๋กœ ์ƒ์„ฑ
  • [...chatData] ์Šคํ”„๋ ˆ๋“œ๋ฅผ ํ†ตํ•ด ์ด๋ ‡๊ฒŒ ์ž‘์—…

  • Object.entries(chatSections) ๊ฐ์ฒด๋ฅผ [ํ‚ค, ๊ฐ’] ํ˜•ํƒœ์˜ ๋ฐฐ์—ด๋กœ ๋ณ€๊ฒฝ
  • StickyHeader position: sticky๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‚ ์งœ ํ‘œ๊ธฐ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ๋‹จ์— ๋ถ™๋„๋ก ๊ณ ์ •ํ•ด๋†“๋Š”๋‹ค.

๐Ÿ”˜ ๋ฆฌ๋ฒ„์Šค ์ธํ”ผ๋‹ˆํŠธ ์Šคํฌ๋กค๋ง(useSWRInfinite)

์Šคํฌ๋กค์„ ์ตœ์ƒ๋‹จ์œผ๋กœ ์˜ฌ๋ ค ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ฌ ์‹œ ๋ฌดํ•œ ์Šคํฌ๋กค ์ ์šฉ ์ž‘์—…

โœ” ์Šคํฌ๋กค์ด ๊ฐ€์žฅ ์ตœ์ƒ๋‹จ ์œ„์น˜ ํ™•์ธ ํ•„์š”

  • ์Šคํฌ๋กค๋ฐ” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ onScrollFrame ๋ฐ์ดํ„ฐ์—์„œ ์Šคํฌ๋กค๋ฐ” ์œ„์น˜๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.

โœ” ์Šคํฌ๋กค์„ ์˜ฌ๋ฆฐ ํ›„ ์ž์‹ ์ด ๋‹ค์‹œ ์ฑ„ํŒ…์„ ์ž…๋ ฅํ•˜์˜€์„ ๋•Œ ์Šคํฌ๋กค์ด ๋งจ ํ•˜๋‹จ์œผ๋กœ ์œ„์น˜ ๋ณ€๊ฒฝ

  • forwardRef Ref๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • DirectMessage์—์„œ ์ปดํฌ๋„ŒํŠธ์—์„œ scrollRef๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.

โœ” SWR์—์„œ Infinite ์ „์šฉ ๋ฉ”์„œ๋“œ ์ œ๊ณต - useSWRInfinite

  • ๊ธฐ์กด useSWR๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ฒซ๋ฒˆ์งธ ์ธ์ž๊ฐ€ ํ•จ์ˆ˜๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.
  • index ํŽ˜์ด์ง€ ์ˆ˜
  • setSize ํŽ˜์ด์ง€ ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.

โœ” Infinite ์Šคํฌ๋กค ์ž‘์—… ์‹œ ๋ฏธ๋ฆฌ ์„ ์–ธ ํ•ด๋‘๋ฉด ์ข‹์€ ๋ฐ์ดํ„ฐ

  • isEmpty : ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์š”์ฒญ ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ

  • isReachingEnd : ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์š”์ฒญ ์‹œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ž”์—ฌ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ


๐Ÿ”˜ ์Šคํฌ๋กค๋ฐ” ์กฐ์ •ํ•˜๊ธฐ

๐Ÿšจ ์Šคํฌ๋กค ๋ฐ”๊ฐ€ ์ตœํ•˜๋‹จ์œผ๋กœ ๋‚ด๋ ค์™€์•ผ ํ•˜๋Š” ์ƒํ™ฉ

  • scrollbarRef.current?.scrollToBottom(); ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์–ด์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒฝ์šฐ์—๋Š” ๋กœ๋”ฉ ์‹œ ์Šคํฌ๋กค ๋ฐ”๊ฐ€ ๊ฐ€์žฅ ํ•˜๋‹จ์œผ๋กœ ์œ„์น˜ํ•˜๋„๋ก ํ•œ๋‹ค.

  • onSubmitForm ์ฑ„ํŒ…์„ ๋ณด๋‚ธ ํ›„์—๋„ ๊ฐ€์žฅ ํ•˜๋‹จ์œผ๋กœ ์œ„์น˜

  • ์ฑ„ํŒ… ์ž…๋ ฅ ํ›„ ์Šคํฌ๋กค์ด ํ•˜๋‹จ์— ๋‚ด๋ ค์˜ค๊ธฐ ๊นŒ์ง€ ์•ฝ๊ฐ„์˜ delay ์†Œ์š”

  • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด mutateChat ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ axios ์š”์ฒญ ์ „ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜์˜๋˜์–ด ํ™”๋ฉด์— ๋‚˜ํƒ€๋„๋ก ์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ UI (Optimistic UI) ๋ฅผ ์ ์šฉํ•˜์˜€๋‹ค.

๐Ÿšจ ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ์˜ฌ ์‹œ ์Šคํฌ๋กค ๋ฐ”๊ฐ€ ์ตœ์ƒ๋‹จ์œผ๋กœ ๋Œ์–ด์ง€๋Š” ์ƒํ™ฉ

  • ๋ฐ์ดํ„ฐ๋ฅผ ๋” ๋ถˆ๋Ÿฌ ์˜ฌ ๋•Œ ์Šคํฌ๋กค๋ฐ”์˜ ์œ„์น˜๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

  • current.scrollTop(current.getScrollHeight() - values.scrollHeight); ์Šคํฌ๋กค๋ฐ”์œ„ ์œ„์น˜ ์œ ์ง€ํ•˜๊ธฐ :
    ์ง€๊ธˆ ํ˜„์žฌ ์Šคํฌ๋กค๋ฐ” ์œ„์น˜ - ์Šคํฌ๋กค๋ฐ”์˜ hegiht


๐Ÿ”˜ DM ์ฑ„ํŒ…ํ•˜๊ธฐ

  • socket.io ์—ฐ๊ฒฐ

  • mutateChat socket.io๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ „๋‹ฌํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— mutateChat ์‚ฌ์šฉ

  • scrollbarRef.current.getScrollHeight() < scrollbarRef.current.getClientHeight() + scrollbarRef.current.getScrollTop() + 150 ์Šคํฌ๋กค๋ฐ”๋ฅผ ์ƒ๋‹จ์„ ์˜ฌ๋ฆด ์‹œ(150px ์ •๋„ ์ด์ƒ์œผ๋กœ ์Šคํฌ๋กค ๋ฐ”๋ฅผ ์˜ฌ๋ ธ์„ ๋•Œ) ์ƒ๋Œ€๋ฐฉ์ด ์ฑ„ํŒ…์„ ๋ณด๋‚ผ ๋•Œ๋Š” ์Šคํฌ๋กค๋ฐ”๊ฐ€ ์•„๋ž˜๋กœ ๋‚ด๋ ค๊ฐ€์ง€ ์•Š๋„๋ก ์ž‘์—…


๐Ÿ”— ์ฐธ๊ณ 

https://jangstory.tistory.com/12
https://inpa.tistory.com/entry/SOCKET-%F0%9F%93%9A-Namespace-Room-%EA%B8%B0%EB%8A%A5

profile
#์˜์š•๋„˜์น˜๋Š”#๐Ÿ’ป#โœจ#FE#๐Ÿ’ช๐Ÿป

0๊ฐœ์˜ ๋Œ“๊ธ€