์ด๋ฒˆ์—๋Š” ๋‘ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€ํ–ˆ๋‹ค:

  • ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ํ”„๋กœํ•„์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” FollowProfile
  • ์ž์ฃผ ์“ฐ๋Š” ๋ฌธ์žฅ์„ ๋“ฑ๋กํ•˜๊ณ  ํ•œ๊ธ€ ์ž์Œ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•ด์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” Shortcut ๊ธฐ๋Šฅ
  • ๋‹จ์ˆœํžˆ UI๋งŒ ๊ตฌ์„ฑํ•œ ๊ฒŒ ์•„๋‹ˆ๋ผ, ์‹ค์ œ API ์—ฐ๋™๋ถ€ํ„ฐ ์ •๋ ฌ ๋กœ์ง, ์ƒํƒœ ์ €์žฅ๊นŒ์ง€ ๊ฝค ๋‹ค์–‘ํ•œ ๊ฐœ๋…์„ ํ•จ๊ป˜ ๋‹ค๋ค˜๋‹ค.

๐Ÿ‘€ FollowProfile: ๋‹ค๋ฅธ ์œ ์ €์˜ ํ”„๋กœํ•„์„ ํŒ์—…์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ

ํŒ”๋กœ์ž‰ํ•œ ์œ ์ €๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ, ํ•ด๋‹น ์œ ์ €์˜ ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ํŒ์—… ํ˜•์‹์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ํ•ต์‹ฌ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ๋ฆ„์ด๋‹ค:

1. ๐Ÿ“ฆ ๋ฐ์ดํ„ฐ ์š”์ฒญ ํ๋ฆ„

const profileWing = GetProfileByUserIDGameIDWing(userId, MAPLEGAMEID);
const profileResponse = await shootingStar.launch<ProfileData>(profileWing);

Wing์œผ๋กœ API ์š”์ฒญ ์ •๋ณด๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ , ShootingStar๋กœ ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค

๋ฐ›์€ ๋ฐ์ดํ„ฐ๋Š” ProfileData ํƒ€์ž…์œผ๋กœ ๊ตฌ์กฐํ™”๋˜์–ด ๋“ค์–ด์˜จ๋‹ค

2. ๐Ÿ“ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•ด์„œ UI์— ํ‘œ์‹œ

  1. ํ”„๋กœํ•„ ์ด๋ฆ„, ๊ฐฑ์‹ ์ผ, ๋Œ€ํ‘œ ์บ๋ฆญํ„ฐ์˜ ๋ ˆ๋ฒจ, ์ „ํˆฌ๋ ฅ, ์œ ๋‹ˆ์˜จ ๋“ฑ์„ ์ถ”์ถœ

  2. getImagePathFromProfile ์œ ํ‹ธ ํ•จ์ˆ˜๋กœ ์บ๋ฆญํ„ฐ ์ด๋ฏธ์ง€ ๊ฒฐ์ •

  3. formatPower()๋กœ ์ „ํˆฌ๋ ฅ ์ˆซ์ž๋ฅผ ์–ต, ์ฒœ๋งŒ ๋‹จ์œ„๋กœ ํฌ๋งทํŒ…

3. ๐Ÿ’ก ํŒ์—… ๋””์ž์ธ๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜

  • Popup์€ ํ™”๋ฉด ํ•˜๋‹จ์— ๊ณ ์ •๋œ ํ˜•ํƒœ๋กœ ๋œจ๋„๋ก position: fixed

  • ์Šฌ๋ผ์ด๋“œ ์—…/๋‹ค์šด ์• ๋‹ˆ๋ฉ”์ด์…˜์€ transform: translateY()์™€ transition์œผ๋กœ ์ œ์–ด

โœ๏ธ Shortcut: ๋‹จ์ถ•์–ด ์ €์žฅ + ํ•œ๊ธ€ ์ž์Œ ์ •๋ ฌ

๐Ÿ“ฅ ๋‹จ์ถ•์–ด ๋“ฑ๋ก ํ๋ฆ„

์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋ฌธ๊ตฌ(input)์™€ ๋‹จ์ถ•์–ด(output)์„ ์ €์žฅํ•˜๊ณ  ์‹ถ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—,
๊ธฐ์กด์˜ ํ”„๋กœํ•„ ์ •๋ณด์— shortcut_words ๋ฐฐ์—ด์„ ์ถ”๊ฐ€ํ•ด์„œ ์ €์žฅํ–ˆ๋‹ค.

const updatedShortcuts = [
  ...(myProfile.shortcut_words || []),
  { input: text, output: shortcut }
];

await updateMyProfile({ shortcut_words: updatedShortcuts });
  • ์ด ์ž‘์—…์€ AddShortcutContainer.tsx์—์„œ ์ฒ˜๋ฆฌ๋˜๋ฉฐ, ์ €์žฅ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰๋จ

  • ์„œ๋ฒ„์—๋Š” updateMyProfile ์œ ํ‹ธํ•จ์ˆ˜๋ฅผ ์žฌํ™œ์šฉํ•ด์„œ ์ „์†ก

๐Ÿ“– ๋‹จ์ถ•์–ด ์ •๋ ฌ ๋กœ์ง: ํ•œ๊ธ€ ์ดˆ์„ฑ ๊ธฐ์ค€

const getHangulInitial = (char) => {
  const initialLetters = ["ใ„ฑ", "ใ„ฒ", "ใ„ด", ...];
  const code = char.charCodeAt(0);

  if (code >= 0xAC00 && code <= 0xD7A3) {
    const index = Math.floor((code - 0xAC00) / 588);
    return initialLetters[index];
  }
  return char;
};

ํ•œ๊ธ€ ์Œ์ ˆ์€ ์œ ๋‹ˆ์ฝ”๋“œ ์ƒ์—์„œ ์ดˆ์„ฑ/์ค‘์„ฑ/์ข…์„ฑ์œผ๋กœ ์กฐํ•ฉ๋˜์–ด ์žˆ๊ณ ,

์ดˆ์„ฑ ํ•˜๋‚˜์˜ ๋‹จ์œ„๋Š” 588๊ฐœ์˜ ๋ธ”๋ก๋งˆ๋‹ค ๋ฐ˜๋ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— Math.floor((code - 0xAC00) / 588)๋กœ ์ดˆ์„ฑ์„ ๊ณ„์‚ฐํ•จ

์ด ๋ฐฉ์‹์œผ๋กœ ๋‹จ์ถ•์–ด๋“ค์„ ใ„ฑ ~ ใ…Ž ์ˆœ์„œ๋กœ ๊ทธ๋ฃนํ™” ๊ฐ€๋Šฅํ•จ

๐Ÿ“‘ ๊ทธ๋ฃนํ™”๋œ ๋ Œ๋”๋ง ๊ตฌ์กฐ

const groupedShortcuts = shortcutList.reduce((acc, shortcut) => {
  const firstChar = getHangulInitial(shortcut.output[0]);
  if (!acc[firstChar]) acc[firstChar] = [];
  acc[firstChar].push(shortcut);
  return acc;
}, {});

๋‹จ์ถ•์–ด ์ถœ๋ ฅ๊ฐ’์˜ ์ฒซ ๊ธ€์ž๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ดˆ์„ฑ์„ ๊ตฌ๋ถ„ํ•˜๊ณ ,

๊ทธ ์ดˆ์„ฑ์„ ํ‚ค๋กœ ํ•ด์„œ ๋ฌถ์–ด ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ

๐Ÿ“ฑ UI ๊ตฌ์„ฑ

๊ทธ๋ฃน ์ด๋ฆ„(์ดˆ์„ฑ)์„ ํฌ๊ฒŒ ๋ณด์—ฌ์ฃผ๊ณ ,

๊ฐ ๋‹จ์ถ•์–ด๋Š” ์ขŒ์ธก์— output, ์šฐ์ธก์— input์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์‹œ๊ฐ์  ๊ตฌ์„ฑ

๐Ÿ” ๊ธฐ์ˆ ์  ํฌ์ธํŠธ ์š”์•ฝ

๊ธฐ๋Šฅ						๊ธฐ์ˆ  ํฌ์ธํŠธ

FollowProfile			Wing + ShootingStar ์กฐํ•ฉ์œผ๋กœ ์œ ์ € ๋ฐ์ดํ„ฐ ์š”์ฒญ ํŒ์—… ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌํ˜„ (translateY + transition)

Shortcut ๋“ฑ๋ก			๊ธฐ์กด ํ”„๋กœํ•„ ๊ตฌ์กฐ ์žฌ์‚ฌ์šฉ (updateMyProfile๋กœ ์ €์žฅ)

์ •๋ ฌ						์œ ๋‹ˆ์ฝ”๋“œ ๊ธฐ๋ฐ˜ ์ดˆ์„ฑ ์ถ”์ถœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ใ„ฑ~ใ…Ž ์ •๋ ฌ ๊ตฌํ˜„

๐Ÿ”œ ๋‹ค์Œ ๋ชฉํ‘œ: ๊ธฐ๋Šฅ ํ™•์žฅ ๋ฐฉํ–ฅ

๐Ÿ’ฌ Shortcut โ†’ ์ฑ„ํŒ… ์ž…๋ ฅ์— ์ž๋™ ์ ์šฉ ์˜ˆ์ •

ํ˜„์žฌ๋Š” ๋‹จ์ถ•์–ด ๋ชฉ๋ก๋งŒ ํ™•์ธ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์ง€๋งŒ,

ํ–ฅํ›„์—๋Š” ์ฑ„ํŒ… ์ž…๋ ฅ๋ž€์—์„œ ๋‹จ์ถ•์–ด๋ฅผ ์ž๋™์œผ๋กœ ํ™•์žฅํ•˜๊ฑฐ๋‚˜,

๋‹จ์ถ•์–ด ์ถ”์ฒœ ๊ธฐ๋Šฅ์ฒ˜๋Ÿผ ์—ฐ๊ฒฐํ•ด์„œ ์‹ค์‹œ๊ฐ„ ์น˜ํ™˜ ๊ธฐ๋Šฅ์„ ๋„ฃ๋Š” ๊ฑธ ๋ชฉํ‘œ๋กœ ํ•˜๊ณ  ์žˆ๋‹ค

์˜ˆ: "ใ„ฑใ……" ์ž…๋ ฅ ์‹œ ์ž๋™์œผ๋กœ "๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค"๋กœ ๋ณ€ํ™˜๋˜๋„๋ก ๊ตฌํ˜„ ์˜ˆ์ •

๐Ÿ” FollowProfile โ†’ ํ”„๋กœํ•„ ํŽ˜์ด์ง€๋กœ ์ด๋™ ๊ฐ€๋Šฅ

ํ˜„์žฌ FollowProfile์€ ํŒ์—…์œผ๋กœ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐ์— ๊ทธ์น˜์ง€๋งŒ,

์œ ์ € ์ด๋ฆ„์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ์œ ์ €์˜ ์ „์ฒด ํ”„๋กœํ•„ ํ™”๋ฉด์œผ๋กœ ๋ผ์šฐํŒ… ๋˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค

onClick={() => navigate(`/trade-profile?userID=${userId}&title=${nickname}`)}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํŒ์—…์—์„œ ์ „์ฒด ํŽ˜์ด์ง€๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐ๋˜๋Š” UX ํ๋ฆ„์ด ์™„์„ฑ๋œ๋‹ค

โœ… ํšŒ๊ณ 

FollowProfile์€ UI/UX ์ธก๋ฉด์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ์ ‘๊ทผ์„ฑ์„ ํ–ฅ์ƒ์‹œ์ผฐ๊ณ ,

Shortcut์€ ์‹ค์‚ฌ์šฉ์ž๊ฐ€ ์ž์ฃผ ์“ฐ๋Š” ๋ฌธ์žฅ์„ ๋น ๋ฅด๊ฒŒ ๋“ฑ๋กํ•˜๊ณ  ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์‹ค์ œ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์ด์—ˆ๋‹ค

ํŠนํžˆ ํ•œ๊ธ€ ์ •๋ ฌ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์œ ๋‹ˆ์ฝ”๋“œ์˜ ๊ตฌ์กฐ๋ฅผ ์ง์ ‘ ๋‹ค๋ค„๋ณธ ๊ฒŒ ํฐ ํ•™์Šต์ด์—ˆ๊ณ , ๋‹จ์ˆœํžˆ ์ •๋ ฌ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ํ•œ๊ตญ์–ด์— ํŠนํ™”๋œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์–ด๋–ป๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์„์ง€ ๊ณ ๋ฏผํ•˜๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ๋‹ค.

๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ ํ•˜๋‚˜์—๋„ API ์„ค๊ณ„, ์ƒํƒœ ์ฒ˜๋ฆฌ, ํ…์ŠคํŠธ ๋ถ„ํ•ด ์•Œ๊ณ ๋ฆฌ์ฆ˜๊นŒ์ง€ ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ ์ด ์Šค๋ฉฐ๋“ ๋‹ค๋Š” ๊ฑธ ๋‹ค์‹œ ์ฒด๊ฐํ•œ ์ž‘์—…์ด์—ˆ๋‹ค.

profile
์ฝ”๋ฆฐ์ด

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

Powered by GraphCDN, the GraphQL CDN