๐Ÿงก Projects/๐Ÿงก Projects: Web

[ ๋ฌด๋ฌผ ] ๊ทผ์ฒ˜ ์‚ฌ์šฉ์ž ์ฐพ๊ธฐ #2 : ์‹ค์‹œ๊ฐ„ ์‚ฌ์šฉ์ž ๊ฐ์ง€ ๋ฐ ์‚ฌ์šฉ์ž ์•„๋ฐ”ํƒ€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

eyes from es 2025. 6. 29. 23:36
728x90
๋ฐ˜์‘ํ˜•
์‚ฌ์šฉ์ž A์™€ B๊ฐ€ ์„œ๋กœ 100m ์ด๋‚ด์— ์žˆ์œผ๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€๋˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ์•„๋ฐ”ํƒ€ ๋ชจ์–‘๋Œ€๋กœ ํ™”๋ฉด์— ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.
๋‹จ์ˆœํžˆ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ A,B๊ฐ€ ์‹œ๊ฐ„ ์ฐจ์ด๋ฅผ ๋‘๊ณ  ์ ‘์†ํ–ˆ์„ ๋•Œ๋„ ๊ฐ์ง€๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

 

๐Ÿงญ ์š”๊ตฌ ์‚ฌํ•ญ

  • ์„œ๋ฒ„๋Š” ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฃผ๋ณ€ ์‚ฌ์šฉ์ž๋“ค์„ ๊ณ„์‚ฐํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ์— ์‹ค์‹œ๊ฐ„ ์ „์†ก
  • ํด๋ผ์ด์–ธํŠธ๋Š” ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ์‚ฌ์šฉ์ž ๋ฆฌ์ŠคํŠธ๋ฅผ ์ƒํƒœ๊ฐ’์œผ๋กœ ์ €์žฅํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ์— ๋ฐ˜์˜
  • ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ณ ์œ ํ•œ ์บ๋ฆญํ„ฐ ํ”„๋กœํ•„์ด ์กด์žฌํ•˜๋ฏ€๋กœ, userId๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ณ„ ์ •๋ณด ์กฐํšŒ API๋ฅผ ์ถ”๊ฐ€ ํ˜ธ์ถœ

 

https://arty6848.tistory.com/189

 

[ ๋ฌด๋ฌผ ] ๊ทผ์ฒ˜ ์‚ฌ์šฉ์ž ์ฐพ๊ธฐ #1 : ์œ„๋„ / ๊ฒฝ๋„ GPS ๊ธฐ๋ฐ˜ WebSocket ์„ธํŒ…ํ•˜๊ธฐ

์‚ฌ์šฉ์ž A์™€ B๊ฐ€ ์„œ๋กœ 10m ์ด๋‚ด์— ์žˆ์œผ๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€๋˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด WebSocket์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.์ฒซ๋ฒˆ์งธ๋กœ ์›น์†Œ์ผ“์„ ์„ธํŒ…ํ•˜๊ณ  ์—ฐ๊ฒฐํ•˜๋Š” ์ดˆ๊ธฐ์„ธํŒ… ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ๐Ÿงญ ์ „์ฒด ๋ชฉํ‘œ ๊ธฐ๋Šฅ‘๋ฌด๋ฌผ

arty6848.tistory.com

 

์šฐ์„  ์ด์ „ ๊ธ€์„ ํ†ตํ•ด ๊ธฐ๋ณธ์ ์ธ ์›น ์†Œ์ผ“ ์„ธํŒ…์„ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.์›น ์†Œ์ผ“์— ์ ‘์†ํ•  ๋•Œ ์ ‘์†ํ•œ ์‚ฌ์šฉ์ž์˜ userId๋ฅผ ์›น ์†Œ์ผ“ ์„œ๋ฒ„์— ์ „์†กํ•˜๊ณ , ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ํ•ด๋‹น userId๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ์ž์˜ ๊ณ ์œ  ์บ๋ฆญํ„ฐ ํ”„๋กœํ•„์„ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

 

์‚ฌ์šฉ์ž ๊ฐ์ง€ ํ๋ฆ„์„ ์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

  participant A as ์‚ฌ์šฉ์ž A
  participant B as ์‚ฌ์šฉ์ž B
  participant Server as WebSocket ์„œ๋ฒ„
  participant DB as ๋ฐฑ์—”๋“œ API

  A->>Server: ์œ„์น˜ ์ •๋ณด ์ „์†ก
  B->>Server: ์œ„์น˜ ์ •๋ณด ์ „์†ก
  Server-->>A: B ๊ฐ์ง€๋จ → userId ์ „์†ก
  Server-->>B: A ๊ฐ์ง€๋จ → userId ์ „์†ก
  A->>DB: userId๋กœ ํ”„๋กœํ•„ ์กฐํšŒ
  A->>DB: userId๋กœ ์ด๋ฆ„ ์กฐํšŒ
  A->>ํ™”๋ฉด: ์•„๋ฐ”ํƒ€์™€ ์ด๋ฆ„ ํ‘œ์‹œ

 

 

์šฐ์„  NextJS์˜ AppRouter ๋ฐฉ์‹์„ ์„ ํƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— 

app/nearby/page.tsx ํŒŒ์ผ์— ๊ธฐ๋ณธ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜์—ฌ /nearby ๊ฒฝ๋กœ์—์„œ ๊ทผ๊ฑฐ๋ฆฌ ํ†ต์‹ ์„ ์œ„ํ•œ ํ™”๋ฉด์„ ๊ฐœ๋ฐœํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ทผ์ฒ˜ ์‚ฌ์šฉ์ž๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ถ€๋ถ„์€ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์ฝ”๋“œ ์ปจ๋ฒค์…˜์„ ๋”ฐ๋ฅด๊ธฐ ์œ„ํ•ด ํ›…์œผ๋กœ ๋ถ„๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 


 

โœจ app/nearby/page.tsx ์ฃผ์š” ํ๋ฆ„ ์ •๋ฆฌ

 

์œ„์น˜ ๊ธฐ๋ฐ˜ ์‚ฌ์šฉ์ž ํƒ์ง€ ๋ฐ ์•„๋ฐ”ํƒ€ ๋ Œ๋”๋ง์˜ ์ค‘์‹ฌ ์ปดํฌ๋„ŒํŠธ

 

 

  • ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฃผ๋ณ€ 100m ์ด๋‚ด ์‚ฌ์šฉ์ž ๊ฐ์ง€
  • WebSocket์„ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ์œ„์น˜ ๊ฐฑ์‹  ์ˆ˜์‹ 
  • ๊ฐ์ง€๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์•„๋ฐ”ํƒ€ UI ๋™์  ๋ Œ๋”๋ง

 

 

โœ…  useSession์œผ๋กœ ๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž ID ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

const { data: session } = useSession();
const userId = session?.user?.id;

 

 

  • WebSocket ์—ฐ๊ฒฐ ์‹œ ์ ‘์†ํ•œ ์‚ฌ์šฉ์ž์˜ ID๋ฅผ ํ•จ๊ป˜ ์„œ๋ฒ„์— ๋ณด๋‚ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ๊ตฌ๋ถ„์„ ์œ„ํ•ด์„  ์œ ์ผํ•œ ์‹๋ณ„์ž๊ฐ€ ํ•„์š”ํ–ˆ๊ณ , NextAuth์˜ ์„ธ์…˜์—์„œ ๋ฐ”๋กœ userId๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

โœ… useRef๋ฅผ ์‚ฌ์šฉํ•ด userId๋ฅผ ๊ธฐ์–ต

const myIdRef = useRef<string | null>(null);

useEffect(() => {
  if (userId) {
    myIdRef.current = userId;
  }
}, [userId]);

 

 

  • ์‹ค์‹œ๊ฐ„ ์†Œ์ผ“ ํ†ต์‹ ์€ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ฆฌ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ์–ด์„œ, userId๋ฅผ ๊ณ„์† prop์œผ๋กœ ๋„˜๊ธฐ๊ธฐ๋ณด๋‹ค๋Š” ref๋กœ ๋ณด์กดํ–ˆ์Šต๋‹ˆ๋‹ค.
  • useRef๋Š” ๋ Œ๋”๋ง์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ๋„ ์ง€์†์ ์œผ๋กœ ๊ฐ’์„ ์œ ์ง€ํ•ด์ค๋‹ˆ๋‹ค.
  • "๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž id๋ฅผ WebSocket ํ†ต์‹  ์ค‘ ๋‚˜ ์ž์‹ ๊ณผ ๊ฐ์ง€๋œ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ref๋กœ ์ €์žฅ"ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

โœ… useNearbySocket ํ›… ์‚ฌ์šฉ

useNearbySocket((data: NearbyUser) => {
  if (
    !data.userId ||
    typeof data.userId !== "string" ||
    data.userId === myIdRef.current
  ) {
    console.warn("๐Ÿ”Ž ์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋ณธ์ธ userId ๋ฌด์‹œ:", data.userId);
    return;
  }

  console.log("๐Ÿ“ก ์ƒˆ ์‚ฌ์šฉ์ž ๊ฐ์ง€:", data.userId);

  setUsers((prev) => {
    const filtered = prev.filter((u) => u.userId !== data.userId);
    return [...filtered, data];
  });
}, userId);

 

 

  • ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ˆ˜์‹ ํ•˜๋ฉด, ํ™”๋ฉด์— ํ‘œ์‹œํ•  ์‚ฌ์šฉ์ž ๋ชฉ๋ก์„ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•ด setUsers๋กœ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๊ธฐ์„œ ๋ณธ์ธ ์ž์‹ ์€ ๊ฐ์ง€ ๋Œ€์ƒ์ด ์•„๋‹ˆ๋ฏ€๋กœ userId === myIdRef.current์ธ ๊ฒฝ์šฐ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.
  • useNearbySocket ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ, ํ•ด๋‹น ๋กœ์ง์ด WebSocket ํ†ต์‹ ์˜ ๋ณต์žกํ•œ ๋กœ์ง์„ ์บก์Аํ™”ํ•˜๊ณ  ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.

โœจ NearbyUserAvatar.tsx ์ฃผ์š” ํ๋ฆ„ ์ •๋ฆฌ

์‚ฌ์šฉ์ž ๊ฐœ๋ณ„ ์•„๋ฐ”ํƒ€ ๋ Œ๋”๋ง ์ปดํฌ๋„ŒํŠธ

 

 

  • ๊ฐ ์‚ฌ์šฉ์ž์˜ ํƒ€์ž…์— ๋งž๋Š” ์•„๋ฐ”ํƒ€ ์ด๋ฏธ์ง€์™€ ์ด๋ฆ„ ํ‘œ์‹œ
  • angle + distance๋ฅผ ์ด์šฉํ•œ ์ƒ๋Œ€์  ์œ„์น˜ ๊ณ„์‚ฐ ๋ฐ ๋ Œ๋”๋ง

 

 

โœ… ์‚ฌ์šฉ์ž ์•„๋ฐ”ํƒ€ ์ด๋ฏธ์ง€ ๋ Œ๋”๋ง

const { data: profile } = useGetUserCharacterProfile(userId);
const imageSrc = profile?.type
  ? `/assets/moono/${profile.type.toLowerCase()}-moono.png`
  : "/assets/moono/default-moono.png";

 

 

  • userId๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ์บ๋ฆญํ„ฐ ํƒ€์ž…(profile.type)์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ๋น„๋™๊ธฐ API ์š”์ฒญ ํ›…์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ด ํƒ€์ž…์— ๋”ฐ๋ผ ์บ๋ฆญํ„ฐ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ๋ฅผ ์œ ๋™์ ์œผ๋กœ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑํ–ฅ ํ…Œ์ŠคํŠธ๋ฅผ ์•„์ง ์ง„ํ–‰ํ•˜์ง€ ์•Š์•„์„œ, ์บ๋ฆญํ„ฐ ํ”„๋กœํ•„์ด ์•„์ง ์—†๋‹ค๋ฉด default ์ด๋ฏธ์ง€๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.

 

โœ… ์‚ฌ์šฉ์ž์˜ ๊ฑฐ๋ฆฌ/๊ฐ๋„์— ๋”ฐ๋ฅธ ์œ„์น˜ ๊ณ„์‚ฐ

const boundedDistance = Math.min(distance * 80, 180);
const x = Math.cos((angle * Math.PI) / 180) * boundedDistance;
const y = Math.sin((angle * Math.PI) / 180) * boundedDistance;

 

  • ์›ํ˜• ๋ฐฉ์‚ฌํ˜• ๊ตฌ์กฐ๋กœ ํ™”๋ฉด์— ์‚ฌ์šฉ์ž ์•„๋ฐ”ํƒ€๋ฅผ ๋ฐฐ์น˜ํ•˜๊ธฐ ์œ„ํ•ด ์‚ผ๊ฐํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•œ ์ขŒํ‘œ ๊ณ„์‚ฐ์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • distance๋Š” ์‹ค์ œ ๊ฑฐ๋ฆฌ(m)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ์ ˆํžˆ ์‹œ๊ฐํ™”๋˜๋„๋ก ๋ณด์ •ํ•˜๊ณ , ์ตœ๋Œ€ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•˜์—ฌ UI๊ฐ€ ๊นจ์ง€์ง€ ์•Š๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

 

๐Ÿ”ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… : ์‚ฌ์šฉ์ž A, B๊ฐ€ ์‹œ๊ฐ„ ์ฐจ๋ฅผ ๋‘๊ณ  ์ ‘์† ์‹œ ๊ฐ์ง€๊ฐ€ ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ

https://github.com/Ureka-Middle-Team1/moo-mool/issues/187

 

[BUG] ๊ทผ๊ฑฐ๋ฆฌ ํ†ต์‹ _์„œ๋กœ ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ ์ƒ๋Œ€๋ฐฉ ํ”„๋กœํ•„ ์ •๋ณด ๊ฐ์ง€ X · Issue #187 · Ureka-Middle-Team1/moo-mo

๐Ÿ“Œ ๋ฌธ์ œ ์ƒํ™ฉ ํ˜„์žฌ ๋™์ผํ•œ ๊ธฐ๊ธฐ์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ์ •๋ณด๋กœ ๋กœ๊ทธ์ธํ•œ ๊ฐ๊ฐ์˜ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ๋ฉด ๊ฐ์ง€๊ฐ€ ์ž˜ ๋˜์ง€๋งŒ ์„œ๋กœ ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ๋Š” ์ƒ๋Œ€๋ฐฉ ํ”„๋กœํ•„ ์ •๋ณด ๊ฐ์ง€ X ๐Ÿ“ธ Screenshot (์Šคํฌ๋ฆฐ์ƒท) โŒ

github.com

 

์‚ฌ์šฉ์ž A์™€ B๊ฐ€ ์„œ๋กœ 100m ์ด๋‚ด์— ์žˆ์œผ๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€๋˜๊ณ , ํ•ด๋‹น ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์•„๋ฐ”ํƒ€๊ฐ€ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅด๊ฒŒ A์™€ B๊ฐ€ ์‹œ๊ฐ„ ์ฐจ๋ฅผ ๋‘๊ณ  ์ ‘์†ํ•˜๋ฉด ์„œ๋กœ๋ฅผ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๋ฐœ์ƒํ•œ ์ƒํ™ฉ

์‹œ๊ฐ„ ์ฐจ๋ฅผ ๋‘๊ณ  ์ ‘์† ์‹œ ์ฃผ๋ณ€ ์นœ๊ตฌ ๊ฐ์ง€X

 

  • ๋ธŒ๋ผ์šฐ์ € A์—์„œ ์‚ฌ์šฉ์ž A ๋กœ๊ทธ์ธ → /nearby ์ง„์ž… → WebSocket ์—ฐ๊ฒฐ ๋ฐ ์œ„์น˜ ์ „์†ก
  • ๋ธŒ๋ผ์šฐ์ € B์—์„œ ์‚ฌ์šฉ์ž B ๋กœ๊ทธ์ธ → /nearby ์ง„์ž… → WebSocket ์—ฐ๊ฒฐ ๋ฐ ์œ„์น˜ ์ „์†ก
  • ์„œ๋ฒ„๋Š” B ์ž…์žฅ ๊ธฐ์ค€์œผ๋กœ ๋ฐ˜๊ฒฝ 100m ์ด๋‚ด ์‚ฌ์šฉ์ž(A) ๋ฅผ ๊ณ„์‚ฐํ•ด B์—๊ฒŒ๋งŒ ์ „์†กํ•จ
  • A๋Š” ์ดํ›„ ์œ„์น˜๋ฅผ ์ „์†กํ•˜์ง€ ์•Š์Œ → ์„œ๋ฒ„๋„ B๋ฅผ A์—๊ฒŒ ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ

 

๊ฒฐ๊ณผ์ ์œผ๋กœ ํ•œ์ชฝ ์‚ฌ์šฉ์ž๋งŒ ์„œ๋กœ๋ฅผ ๊ฐ์ง€ํ•˜๊ฒŒ ๋˜์–ด ํ™”๋ฉด์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋œจ์ง€ ์•Š๊ฑฐ๋‚˜, ์–‘๋ฐฉํ–ฅ ๊ฐ์ง€๊ฐ€ ๋˜์ง€ ์•Š์Œ.

 

 

๐Ÿ” ์›์ธ ๋ถ„์„

์ฒ˜์Œ์—๋Š” ์‚ฌ์šฉ์ž ์œ„์น˜๋ฅผ WebSocket ์„œ๋ฒ„์— ์ „์†กํ•  ๋•Œ, getCurrentPosition()์„ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์„œ๋ฒ„์— ํ•œ ๋ฒˆ๋งŒ ์ „์†กํ–ˆ์Šต๋‹ˆ๋‹ค.

 

ํด๋ผ์ด์–ธํŠธ

// [๋ฌธ์ œ ์ฝ”๋“œ - useNearbySocket.ts]
navigator.geolocation.getCurrentPosition(
  (position) => {
    const { latitude, longitude } = position.coords;
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(
        JSON.stringify({
          type: "location_update",
          userId,
          lat: latitude,
          lng: longitude,
        })
      );
    }
  },
  (error) => {
    console.error("๐Ÿ“ ์œ„์น˜ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ:", error.message);
  }
);

 

useNearbySocket์€ ์‚ฌ์šฉ์ž์˜ ์œ„์น˜๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ WebSocket ์„œ๋ฒ„์— ์ „์†กํ•˜๊ณ , ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ฃผ๋ณ€ ์‚ฌ์šฉ์ž ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ์ปค์Šคํ…€ ํ›…์ž…๋‹ˆ๋‹ค.

์ด ํ›… ๋‚ด๋ถ€์—์„œ getCurrentPosition์ด ๋‹จ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. ์‚ฌ์šฉ์ž A๊ฐ€ /nearby์— ์ง„์ž… → ์œ„์น˜ ํ•œ ๋ฒˆ ์ „์†ก ํ›„ ๋
2. ์‚ฌ์šฉ์ž B๊ฐ€ ๋‚˜์ค‘์— ์ง„์ž… → ์œ„์น˜ ์ „์†ก → ์„œ๋ฒ„๋Š” B ๊ธฐ์ค€์œผ๋กœ๋งŒ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ
3. ์„œ๋ฒ„๋Š” B์—๊ฒŒ๋งŒ A๋ฅผ ๋ณด๋‚ด์ฃผ๊ณ , A๋Š” ์—…๋ฐ์ดํŠธ ์—†์Œ → ๊ฒฐ๊ตญ A๋Š” ์•„๋ฌด๋„ ๋ชป ๊ฐ์ง€ํ•จ

 

๊ฒฐ๊ณผ์ ์œผ๋กœ ์–‘๋ฐฉํ–ฅ ๊ฐ์ง€๊ฐ€ ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค

 

 

์„œ๋ฒ„์ฝ”๋“œ

// [๋ฌธ์ œ ์ฝ”๋“œ - server.js]
ws.on("message", (message) => {
  const parsed = JSON.parse(message);
  if (parsed.type === "location_update") {
    // ์„œ๋ฒ„ ๋‚ด๋ถ€์— ์‚ฌ์šฉ์ž ์œ„์น˜๋งŒ ์ €์žฅํ•˜๊ณ  ๋
    userLocations[parsed.userId] = {
      lat: parsed.lat,
      lng: parsed.lng,
    };

    // โŒ ๊ฐ์ง€๋œ ์‚ฌ์šฉ์ž๋ฅผ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด๋‚ด์ฃผ๋Š” ๋ถ€๋ถ„์ด ์—†์Œ
  }
});

 

 

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์œ„์น˜๋ฅผ ๋ณด๋‚ด๋”๋ผ๋„ ๊ฐ์ง€๋œ ์ฃผ๋ณ€ ์‚ฌ์šฉ์ž ๋ฆฌ์ŠคํŠธ๋ฅผ ๋Œ๋ ค์ฃผ์ง€ ์•Š์Œ, ์ฆ‰ ์œ„์น˜ ์ €์žฅ๋งŒ ํ•˜๊ณ  ๊ฐ์ง€ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋‚ด์ง€ ์•Š์Œ
  • ๊ฒฐ๊ตญ A๋Š” ์„œ๋ฒ„์— ์œ„์น˜๋ฅผ ๋ณด๋ƒˆ์ง€๋งŒ, ์•„๋ฌด ์‘๋‹ต์„ ๋ชป ๋ฐ›์Œ

 

 

โœ… ๊ฐœ์„ : ์œ„์น˜๋ฅผ 1์ดˆ๋งˆ๋‹ค ๋ฐ˜๋ณต ์ „์†ก

 

ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ

// [์ˆ˜์ •๋œ ์ฝ”๋“œ - useNearbySocket.ts]
const sendLocation = () => {
  navigator.geolocation.getCurrentPosition(
    (position) => {
      const { latitude, longitude } = position.coords;
      if (wsRef.current?.readyState === WebSocket.OPEN) {
        wsRef.current.send(
          JSON.stringify({
            type: "location_update",
            userId,
            lat: latitude,
            lng: longitude,
          })
        );
      }
    },
    (error) => {
      console.error("๐Ÿ“ ์œ„์น˜ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ:", error.message);
    }
  );
};

sendLocation(); // ์ตœ์ดˆ 1ํšŒ ์ „์†ก
intervalId = setInterval(sendLocation, 1000); // ์ดํ›„ 1์ดˆ๋งˆ๋‹ค ์ „์†ก
  • ์œ„์น˜ ์ „์†กํ•˜๋Š” ๋ถ€๋ถ„์„ sendLocation ํ•จ์ˆ˜๋กœ ์ง€์ •ํ•˜๊ณ  setInterval๋กœ 1์ดˆ๋งˆ๋‹ค ๋ฐ˜๋ณตํ•˜์—ฌ ์ „์†กํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž A, B ๋ชจ๋‘ ์ž์‹ ์˜ ์œ„์น˜๋ฅผ 1์ดˆ๋งˆ๋‹ค ์„œ๋ฒ„์— ๊ฐฑ์‹ ํ•˜๊ฒŒ ๋จ
  • ์„œ๋ฒ„๋Š” ์ตœ์‹  ์œ„์น˜ ๊ธฐ์ค€์œผ๋กœ ์ฃผ๋ณ€ ์‚ฌ์šฉ์ž๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ์–‘๋ฐฉํ–ฅ ์ „์†ก ๊ฐ€๋Šฅ

 

์„œ๋ฒ„ ์ฝ”๋“œ : ์‹ค์‹œ๊ฐ„ ์‘๋‹ต ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ

// [์ˆ˜์ •๋œ ์ฝ”๋“œ - server.js]
ws.on("message", (message) => {
  const parsed = JSON.parse(message);

  if (parsed.type === "location_update") {
    userLocations[parsed.userId] = {
      lat: parsed.lat,
      lng: parsed.lng,
    };

    // ์ฃผ๋ณ€ ์‚ฌ์šฉ์ž ๊ณ„์‚ฐ
    const nearbyUsers = getNearbyUsers(parsed.userId, userLocations);

    // ์ฃผ๋ณ€ ์‚ฌ์šฉ์ž ์ •๋ณด ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ
    for (const client of clients) {
      if (client.readyState === WebSocket.OPEN) {
        client.send(
          JSON.stringify({
            type: "nearby_users",
            users: nearbyUsers,
          })
        );
      }
    }
  }
});

 

 

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ "location_update"๋ผ๋Š” ํƒ€์ž…์œผ๋กœ ์ „์†กํ•˜๋ฉด 
  • ํ•ด๋‹น ๋ฉ”์„ธ์ง€ ํƒ€์ž…์„ ๋ฐ›์•˜์„ ๋•Œ nearbyUsers์— ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์‚ฌ์šฉ์ž๋ฅผ ์ €์žฅํ•œ ํ›„, ์›น ์†Œ์ผ“์— ์ ‘์†ํ•œ ์‚ฌ์šฉ์ž (์›น ์†Œ์ผ“์ด OPEN ์ƒํƒœ์ธ clients)์—๊ฒŒ nearby_users๋ผ๋Š” ๋ฉ”์„ธ์ง€ ํƒ€์ž…์œผ๋กœ ๊ทผ์ฒ˜ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ชจ๋‘ ๋ณด๋‚ด์ค๋‹ˆ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ๊ฐ„์ฐจ๋ฅผ ๋‘๊ณ  ์ ‘์†ํ•˜๋”๋ผ๋„ ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค "location_update" ํƒ€์ž…์˜ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด๊ฒŒ ๋˜๊ณ  ์œ„์˜ ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๊ทธ ๊ฒฐ๊ณผ...

 

728x90
๋ฐ˜์‘ํ˜•