GithubHelp home page GithubHelp logo

collabu / pennyway-client-webview Goto Github PK

View Code? Open in Web Editor NEW
13.0 1.0 1.0 4.94 MB

๐Ÿช™ Pennyway Webview Client: A web browser that is embedded in a pennyway iOS mobile client

JavaScript 5.59% HTML 0.25% TypeScript 82.23% Dockerfile 0.07% SCSS 11.80% Shell 0.06%
react webview typescript fsd vitest zustand

pennyway-client-webview's Introduction

Pennyway

๐Ÿ’ฐ Pennyway

์ง€์ถœ ๊ด€๋ฆฌ SNS ํ”Œ๋žซํผ

Pennyway๋Š” ์†Œ๋น„๋ฅผ ์ธก์ •ํ•˜๊ณ  ๋ถˆํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” SNS ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋“ค์€ Pennyway๋ฅผ ํ†ตํ•ด ์ง€์ถœ์„ ๊ด€๋ฆฌํ•˜๊ณ , ์†Œ๋น„ ์Šต๊ด€์„ ๋ถ„์„ํ•˜๋ฉฐ, ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋“ค๊ณผ ์†Œ๋น„ ๊ด€๋ จ ์ •๋ณด๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Pennyway๋Š” ๊ฐœ์ธ๊ณผ ๊ฐ€๊ณ„๋ถ€, ๊ทธ๋ฆฌ๊ณ  ์‚ฌํšŒ์ ์ธ ์†Œ๋น„ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ํ”Œ๋žซํผ์œผ๋กœ์„œ, ๋” ๋‚˜์€ ์†Œ๋น„ ์Šต๊ด€์„ ํ˜•์„ฑํ•˜๊ณ  ์žฌ์ •์ ์ธ ์•ˆ์ •์„ ๋„๋ชจํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค.

Version # Revision Date Description Author
v0.0.1 2024.03.11 ํ”„๋กœ์ ํŠธ ๊ธฐ๋ณธ ์„ค๋ช… ์ž‘์„ฑ ์ด์˜์ฐฌ
v0.0.2 2024.05.06 ํ”„๋กœ์ ํŠธ ํ™˜๊ฒฝ ์„ค์ • ๊ฐ•๋ณ‘์ค€

๐Ÿ‘ช Webview Team

์ด์˜์ฐฌ ๊ฐ•๋ณ‘์ค€

๐Ÿ“Œ Architecture

1๏ธโƒฃ FSD Architecture


2๏ธโƒฃ CI/CD Pipeline

ci pipeline cd pipeline

๐Ÿ“— Gruond Rule

๐Ÿค Convention

โš™๏ธ Software Design - Frontend

pennyway-client-webview's People

Contributors

bangdori avatar legitgoons avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

legitgoons

pennyway-client-webview's Issues

๐Ÿ› ํฐํŠธ ๋””์ฝ”๋”ฉ ์‹คํŒจ

๋ฒ„๊ทธ ๋‚ด์šฉ

  • ํฐํŠธ ๋””์ฝ”๋”ฉ ์‹คํŒจ

๋ฒ„๊ทธ ์œ„์น˜

  • public/assets/font
  • src/app/styles/_fonts.scss

์žฌํ˜„

image

ํ• ์ผ

  • ์ •์ƒ์ ์œผ๋กœ font์˜ ๋””์ฝ”๋”ฉ์ด ์ด๋ค„์ง€๋„๋ก ์ˆ˜์ •

๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ ๋ฆฌํŒฉํ† ๋ง (NetworkErrorToast, Carousel)

  • NetworkErrorToast ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ ์ตœ์ƒ๋‹จ์— ์œ„์น˜ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์žฌ์‚ฌ์šฉ ์ปดํฌ๋„ŒํŠธ๋Š” ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— app/ ์ด๋™
  • carousel์˜ ๊ฒฝ์šฐ ํ”ผ๋“œ์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— widgets/feed-carousel๋กœ ์ด๋™

๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ ์ˆ˜์ •

  • ActiveButton ๋ฐ BasicButton์˜ ๋ชจํ˜ธํ•œ ์‚ฌ์šฉ์ฒ˜๋กœ ์ธํ•œ ํ•ด๋‹น ๋ฒ„ํŠผ๋“ค ์ œ๊ฑฐ
  • (์ถ”๊ฐ€ ์ž‘์—…) ConfirmReportModal ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋ง
  • ๋ชจ๋‹ฌ Small ๋ฒ„ํŠผ ๋ฐ ํŒ”๋กœ์ž‰ ๋ฒ„ํŠผ ์ถ”๊ฐ€
  • ConfirmModal ์Šคํƒ€์ผ์— ๋ถ„๊ธฐ๋ฌธ ์ •๋ฆฌ

๐Ÿ› Profile ํŽ˜์ด์ง€ ์Šคํฌ๋กค ๋ฒ„๊ทธ

๋ฒ„๊ทธ ๋‚ด์šฉ

  • Profile ํŽ˜์ด์ง€ ์Šคํฌ๋กค์ด ์ตœํ•˜๋‹จ์—์„œ ์‹œ์ž‘

๋ฒ„๊ทธ ์œ„์น˜

  • src/pages/profile/ui/ProfileMainPage.tsx

์žฌํ˜„

  1. iPhone-layout์˜ ๊ฒฝ์šฐ
  • Feed์˜ ๊ฒŒ์‹œ๊ธ€ ์ค‘ ์ตœ์ƒ์œ„ ๊ฒŒ์‹œ๊ธ€(Feed Content 1 by ๊ฐ•๋ณ‘์ค€)์ด ์•„๋‹ˆ๋ผ๋ฉด ์ตœํ•˜๋‹จ์—์„œ ์‹œ์ž‘
    image
  1. ์ผ๋ฐ˜ ํ™”๋ฉด์˜ ๊ฒฝ์šฐ
    • Feed์˜ ๊ฒŒ์‹œ๊ธ€ ์ค‘ ์ตœ์ƒ์œ„ ๊ฒŒ์‹œ๊ธ€์ด ์•„๋‹ˆ๊ณ , ์ฒซ ๋ฐฉ๋ฌธ์ด ์•„๋‹ ๊ฒฝ์šฐ(์ด๋ฏธ ๋žœ๋”๋ง ๋œ ํŽ˜์ด์ง€์ผ ๊ฒฝ์šฐ) ์ตœํ•˜๋‹จ์—์„œ ์‹œ์ž‘
  • ์ถ”์ธก๋˜๋Š” ์›์ธ
    • ์ด์ „ ํŽ˜์ด์ง€์—์„œ์˜ ์Šคํฌ๋กค ์œ„์น˜๊ฐ€ ์œ ์ง€๋˜๋Š” ๊ฒƒ์œผ๋กœ ์ถ”์ธก๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋’ค๋กœ ๊ฐ€๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ๊ฒฝ์šฐ ์Šคํฌ๋กค ์œ„์น˜๊ฐ€ ์ตœ์ƒ๋‹จ์ด ๋˜๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ ์„ค์ •์ธ ๊ฒƒ์œผ๋กœ ์•Œ๊ธฐ์—, ์กฐ๊ธˆ ๋” ์ฐพ์•„๋ณผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋˜ํ•œ iPhone-layout์˜ ๊ฒฝ์šฐ์—๋Š” ์ตœ์ƒ์œ„ ํ”ผ๋“œ์˜ Profile์ด ์•„๋‹ˆ๋ผ๋ฉด ์ฒ˜์Œ ๋ฐฉ๋ฌธํ•˜๋Š” ํŽ˜์ด์ง€๋„ ๋Š˜ ์ตœํ•˜๋‹จ์—์„œ ์‹œ์ž‘๋˜๊ธฐ์— iPhone-Layout์˜ ๊ฒฝ์šฐ ๋‹ค๋ฅธ ์›์ธ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ• ์ผ

  • ProfilePage์˜ ์Šคํฌ๋กค์ด ์ตœ์ƒ๋‹จ์—์„œ ์‹œ์ž‘๋˜๋„๋ก ์ˆ˜์ •

๐Ÿ”ง Mutation ์ดํ›„ ์„œ๋ฒ„ ์ƒํƒœ ๋™๊ธฐํ™” ์œ ์ง€

์ˆ˜์ • ๋ชฉ์ 

Mutation ์ดํ›„์— ์„œ๋ฒ„ ์ƒํƒœ ๋™๊ธฐํ™” ์œ ์ง€๋ฅผ ์œ„ํ•œ ๋ฆฌํŒฉํ† ๋ง

์ˆ˜์ • ์ฝ”๋“œ

  • src/features/feed-bookmark/api/useBookmarks.tsx
  • src/features/feed-main-like/api/useLikes.tsx

ํ•  ์ผ ๋ชฉ๋ก

  • Mutation ์ดํ›„ ์„œ๋ฒ„ ์ƒํƒœ ๋™๊ธฐํ™”

์„ค๋ช…

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ•œ ์ดํ›„ API ์š”์ฒญ์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด ํ˜„์žฌ ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

export const useLikes = (feedId: number, isLiked: boolean) => {
  const queryClient = useQueryClient();

  const { mutate: handleLikeFeed, isPending } = useMutation({
    mutationFn: () => isLiked ? requestUnlikeFeed(feedId) : requestLikeFeed(feedId),
    onMutate: () => {
      // ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ
    }
    onError: (_, __, context) => {
      // Network Error์ผ ๊ฒฝ์šฐ ์ด์ „ ์ฟผ๋ฆฌ๊ฐ’์œผ๋กœ ๋กค๋ฐฑ
      queryClient.setQueryData([QUERY_KEYS.feeds], context?.previousQueryData);
    },
    onSuccess: (response, _, context) => {
      if (isErrorResponse(response)) {
        // ์„œ๋ฒ„ ์‘๋‹ต ์‹คํŒจ ์‹œ ์ด์ „ ์ฟผ๋ฆฌ๊ฐ’์œผ๋กœ ๋กค๋ฐฑ
        queryClient.setQueryData([QUERY_KEYS.feeds], context.previousQueryData);
      }
    },
  });

  return { handleLikeFeed, isPending };
};

ํ•˜์ง€๋งŒ ์ด ๊ฒฝ์šฐ์—๋Š” ์น˜๋ช…์ ์ธ ๋ฌธ์ œ์ ์ด ํ•˜๋‚˜ ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ ์„œ๋ฒ„์™€์˜ ๋™๊ธฐํ™”๊ฐ€ ์ง„ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

๐Ÿ”ง CD Pipeline์˜ ์ ์šฉ ์‹œ์ 

๋…ผ์˜ ์ฃผ์ œ

  • CD Pipeline์˜ ์ ์šฉ ์‹œ์ 

๋…ผ์˜ ๋‚ด์šฉ

ํ˜„์žฌ github flow์— ๋งž์ถฐ main๋ธŒ๋Ÿฐ์น˜์™€ ์ž‘์—… ๋ธŒ๋Ÿฐ์น˜ ๋‘๊ฐœ์˜ ๋ธŒ๋Ÿฐ์น˜๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. CI ํŒŒ์ดํ”„๋ผ์ธ์€ main ๋ธŒ๋žœ์น˜์— pull request๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์„ ๋•Œ trigger ๋˜๊ณ , CD ํŒŒ์ดํ”„๋ผ์ธ์€ main ๋ธŒ๋žœ์น˜์— push ๋˜์—ˆ์„ ๋•Œ trigger ๋ฉ๋‹ˆ๋‹ค.

๋…ผ์˜ํ•˜๊ณ ์ž ํ•˜๋Š” ์ ์€ CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์–ธ์ œ ์ ์šฉ์‹œ์ผœ์•ผ ํ•˜๋Š”๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋งŒ์•ฝ push๋˜์—ˆ์„ ๋•Œ CD Pipeline์ด fail ๋˜๋ฉด ์ตœ์‹  ๋ฒ„์ „์ด ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด์„œ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์•ผ ํ• ๊นŒ์š”?

์ œ๊ฐ€ ์ƒ๊ฐํ•œ ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์šฐ์„  ์ œ๊ฐ€ ์ƒ๊ฐํ•œ ๋ฐฉ๋ฒ•์€ ์œ„ 3๊ฐ€์ง€ ์ธ๋ฐ, ํ˜น์‹œ ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค๋ฉด ์˜๊ฒฌ์„ ๋‚จ๊ฒจ์ฃผ์…”๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!

๐Ÿ”ง z-index๊ฐ’ ์ˆ˜์ •

์ˆ˜์ • ๋ชฉ์ 

  • ๋ถˆ๋ถ„๋ช…ํ•œ z-index๊ฐ’ ํ†ต์ผ

์ˆ˜์ • ์ฝ”๋“œ

  • src/shared/ui/backdrop/Backdrop.scss
  • src/shared/ui/modal/ModalOverlay.scss
  • ๊ธฐํƒ€ z-index๊ฐ’ ์‚ฌ์šฉ scssํŒŒ์ผ

ํ•  ์ผ ๋ชฉ๋ก

  • ์–ด๋–ค ํŒŒ์ผ์ด ์œ„์— ์ถœ๋ ฅ๋˜์–ด์•ผ ํ• ์ง€ ๊ณ„์ธต๋ณ„ ๋ถ„๋ฅ˜
  • ๊ณ„์ธต์— ๋”ฐ๋ผ z-index๊ฐ’ ์ˆ˜์ •

์„ค๋ช…

  • ๊ณ„์ธต์— ๋”ฐ๋ผ z-index๊ฐ’์ด ํ†ต์ผ๋˜์–ด ์žˆ์ง€ ์•Š์•„, ์˜ค๋ฅ˜๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ ์กด์žฌ

๐Ÿ”ง SVG Sprite๋ฅผ ํ™œ์šฉํ•œ SVG ๊ด€๋ฆฌ

์ˆ˜์ • ๋ชฉ์ 

SVG Sprite ์ ์šฉ

์ˆ˜์ • ์ฝ”๋“œ

import BookmarkIcon from '@/shared/ui/icon/assets/bookmark_icon.svg?react';
import ChatIcon from '@/shared/ui/icon/assets/chat_icon.svg?react';
import KebabMenuIcon from '@/shared/ui/icon/assets/kebab_menu_icon.svg?react';
import LikeIcon from '@/shared/ui/icon/assets/like_icon.svg?react';
import SharedIcon from '@/shared/ui/icon/assets/share_icon.svg?react';

ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ์œ„์™€ ๊ฐ™์ด SVG๋ฅผ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์œ„ ๋ถ€๋ถ„์„ ์•„๋งˆ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

import SvgIcon from '@/shared/ui';

<Icon icon="bookmark" size="20" color="red" />

ํ•  ์ผ ๋ชฉ๋ก

  • SVG Box์˜ ํฌ๊ธฐ๋ฅผ ๋””๋ฐ”์ด์Šค์— ๋งž๊ฒŒ ๋ฐ˜์‘ํ˜• ๊ตฌ์„ฑ - ์ผ๊ด€์„ฑ ๋ณด์žฅ
  • SVG Sprite ๊ธฐ๋ฒ• ์ ์šฉ - ์ผ๊ด€์„ฑ ๋ฐ Public API ์ ์šฉ

์„ค๋ช…

ํ˜„์žฌ SVG๋ฅผ ์‚ฌ์šฉํ•จ์— ์žˆ์–ด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ผ๊ด€์„ฑ์žˆ๊ฒŒ ๊ด€๋ฆฌ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค.
  2. ํ˜„์žฌ ์•„ํ‚คํ…์ฒ˜์—์„œ ์ถ”๊ตฌํ•˜๋Š” Public API์— ๋Œ€ํ•œ ์ ์šฉ์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๊ณ  ์žˆ๋‹ค.

์‹ค์ œ๋กœ ์•„์ด์ฝ˜์„ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์ด๋Ÿฐ์‹์œผ๋กœ svg ์œ„์น˜ ์ž์ฒด๋ฅผ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

โœจ ํ”ผ๋“œ ์‹ ๊ณ ํ•˜๊ธฐ UI ์ˆ˜์ •

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

  • ์‹ ๊ณ ํ•˜๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ ํ›„ UI ํ˜น์€ ํ† ์ŠคํŠธ ๋ Œ๋”๋ง
  • ์‹ ๊ณ ํ•˜๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ์— ๋Œ€ํ•œ UI ๋ฐ ์„œ๋ฒ„ ์žฌ์š”์ฒญ ๊ธฐ๋Šฅ ๋“ฑ

ํ•  ์ผ ๋ชฉ๋ก

  • ์‹ ๊ณ ํ•˜๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ "์‹ ๊ณ ๊ฐ€ ์ ‘์ˆ˜๋˜์—ˆ์–ด์š”" ํ† ์ŠคํŠธ ํ‘œ์‹œ
  • ํ•ด๋‹น ๊ฒŒ์‹œ๋ฌผ ์ˆจ๊ธฐ๊ธฐ ํด๋ฆญ ํ›„, ์‹ ๊ณ ํ•˜๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๋ฐ ๊ฒŒ์‹œ๋ฌผ ์ˆจ๊ธฐ๊ธฐ
  • ์‹ ๊ณ ํ•˜๊ธฐ ์ฒดํฌ๋ฐ•์Šค ์ฒดํฌ๋˜์–ด ์žˆ์ง€ ์•Š์€ ์ƒํƒœ๋กœ ์‹œ์ž‘
  • ์‹ ๊ณ ํ•˜๊ธฐ ์‹คํŒจ ์‹œ, ๊ธฐ์กด ์ƒํƒœ ์œ ์ง€ ๋ฐ "๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”" ํ† ์ŠคํŠธ ํ‘œ์‹œ

์„ค๋ช…

ํ”ผ๋“œ ์‹ ๊ณ ํ•˜๊ธฐ ๊ธฐ๋Šฅ์˜ UI๊ฐ€ ํ˜„์žฌ ์ˆ˜์ •์ค‘์— ์žˆ์–ด UI ์ˆ˜์ • ์ž‘์—…์— ๋Œ€ํ•ด ์ด์Šˆ๋กœ ๋“ฑ๋กํ•ด๋‘๊ณ , UI๊ฐ€ ์™„์„ฑ๋˜๋Š” ๋Œ€๋กœ ๋ฐ˜์˜ ์˜ˆ์ •

Virtual Scroll

image

ํ˜„์žฌ ํ™”๋ฉด์— ๋ณด์—ฌ์ง€๋Š” ํ”ผ๋“œ์˜ ๊ฐœ์ˆ˜์™€ ๋‹ฌ๋ฆฌ, ๋ชจ๋“  ํ”ผ๋“œ ๋ฐ์ดํ„ฐ๊ฐ€ DOM์— ๊ทธ๋ ค์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์•„์ดํฐ์„ ์‚ฌ์šฉํ•˜๋Š” ์œ ์ €์˜ ์ž…์žฅ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ํ–ฅ์„ ๋ผ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์„ฑ๋Šฅ ์ €ํ•˜
  2. ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์ฆ๊ฐ€
  3. ์‘๋‹ต์„ฑ ์ €ํ•˜ -> ์•ฑ ๋ฉˆ์ถค

์ด๋ฅผ Virtual Scroll์„ ๊ธฐ๋ฒ•์„ ์ ์šฉํ•˜์—ฌ ํ™”๋ฉด์— ๋ณด์ด๋Š” ๋ถ€๋ถ„๋งŒ ๋™์ ์œผ๋กœ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋„๋ก
์ตœ์ ํ™” ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

โœจ User API ๋‹‰๋„ค์ž„ / ์ด๋ฆ„ ๋ถ„๋ฆฌ

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

  • User API์— ๋‹‰๋„ค์ž„๊ณผ ์ด๋ฆ„ ์ถ”๊ฐ€
  • ์ถ”๊ฐ€ํ•œ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๊ธฐ์กด ๋กœ์ง ์ˆ˜์ •

ํ•  ์ผ ๋ชฉ๋ก

  • User API ๋‹‰๋„ค์ž„, ์ด๋ฆ„ ๋ถ„๋ฆฌ
  • ๊ธฐ์กด User API ์‚ฌ์šฉ ๋กœ์ง ์ˆ˜์ •

์„ค๋ช…

  • User API์— ๋‹‰๋„ค์ž„๊ณผ ์ด๋ฆ„์ด ๋ณ„๊ฐœ๋กœ ๋˜์–ด ์žˆ์ง€์•Š๊ณ  name์ด๋ผ๋Š” ํ•˜๋‚˜์˜ ํ‚ค์›Œ๋“œ๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”ง Basic, Active ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ํฐํŠธ ์„ค์ •

์ˆ˜์ • ๋ชฉ์ 

ํ˜„์žฌ shared์— ์œ„์น˜ํ•œ ๊ณต์šฉ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๋“ค์— ๋Œ€ํ•œ ํฐํŠธ ์ ์šฉ

์ˆ˜์ • ์ฝ”๋“œ

ํ•  ์ผ ๋ชฉ๋ก

  • ๋ฒ„ํŠผ ํด๋ž˜์Šค๋“ค์— ๋Œ€ํ•œ ํฐํŠธ ํด๋ž˜์Šค ์ถ”๊ฐ€

์„ค๋ช…

#31 ์—์„œ ํฐํŠธ ํด๋ž˜์Šค๊ฐ€ ์ค‘๋ณต์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ฉด์„œ, ๊ธฐ์กด์— Basic ๋ฒ„ํŠผ๊ณผ Active ๋ฒ„ํŠผ ํด๋ž˜์Šค์— ์ ์šฉ๋˜๊ณ  ์žˆ์—ˆ๋˜ ํฐํŠธ๋“ค์ด ์ œ๊ฑฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๊ฐ ๋ฒ„ํŠผ ํด๋ž˜์Šค๋“ค์— ๋Œ€ํ•ด ํฐํŠธ ํด๋ž˜์Šค๋ฅผ ์ ์šฉ ํ•„์š”

๐Ÿ”ง ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋„๊ตฌ ๋ณ€๊ฒฝ

์ˆ˜์ • ๋ชฉ์ 

์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฅผ ๋”์šฑ ํšจ์œจ์ ์œผ๋กœ ํ•จ๊ณผ ๋™์‹œ์— Zero-install์„ ํ†ตํ•œ Build time ์ฆ๊ฐ€

์ˆ˜์ • ๋‚ด์šฉ

  • node_modules -> yarn PnP

ํ•  ์ผ ๋ชฉ๋ก

  • node_modules -> yarn PnP ๋ณ€๊ฒฝ
  • zero-install ์„ค์ •
  • CI/CD Pipeline ์ˆ˜์ •

์„ค๋ช…

์•„์ง๊นŒ์ง€๋Š” ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ํฌ์ง€ ์•Š์•„, node_modules์„ ์ด์šฉํ•œ ์˜์กด์„ฑ ๊ด€๋ฆฌ์™€ yarn PnP๋ฅผ ์ด์šฉํ•œ ์˜์กด์„ฑ ๊ด€๋ฆฌ์— ํฐ ์ฐจ์ด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง์— ๋”ฐ๋ผ node_modules๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์˜์กด์„ฑ ๊ด€๋ฆฌ์— ํฐ ๋ณต์žก์„ฑ์ด ๋”ฐ๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด์— yarn PnP๋ฅผ ํ†ตํ•ด ๋”์šฑ ํšจ์œจ์ ์ธ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฅผ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ yarn PnP๋ฅผ ์ด์šฉํ•˜๊ฒŒ ๋˜๋ฉด zero-install์„ ๋„์ž…ํ•  ์ˆ˜ ์žˆ์–ด, CI/CD Build Time์˜ ์ฆ๊ฐ€๊นŒ์ง€ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ค‘์š”ํ•˜์ง€๋งŒ ๊ธด๊ธ‰ํ•˜์ง€๋Š” ์•Š์€ ์‚ฌํ•ญ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์—ฌ, Jira Confluence table์— (์ค‘์š”ํ•จ-๊ธด๊ธ‰ํ•˜์ง€ ์•Š์Œ)์— ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ๋ฌธ์„œ: https://toss.tech/article/node-modules-and-yarn-berry

๐Ÿ› BottomSheetModal ๊ตฌ๋ถ„์„  ์˜ค๋ฅ˜

๋ฒ„๊ทธ ๋‚ด์šฉ

image

  • BottomSheetModal์˜ ๊ตฌ๋ถ„์„ ์ด 0.5px๋กœ ์ •์˜๋˜์–ด ์žˆ์Œ, ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ €์—์„œ 0.5px๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ ๋˜์ง€ ์•Š์Œ

๋ฒ„๊ทธ ์œ„์น˜

  • shared/ui/modal/BottomSheetModal
  • widgets/feed-main-list/ui/FeedMainList.scss
  • widgets/feed-main-list/ui/SkeletonFeedMainList.scss

ํ• ์ผ

  • ์—๋Ÿฌ ์›์ธ ํŒŒ์•… ํ›„ ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ๋˜๋„๋ก ์ˆ˜์ •
  • App ๋‚ด๋ถ€์—์„œ๋„ ๊ฐ™์€ ํ˜„์ƒ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํ™•์ธ
  • ๋งŒ์•ฝ 0.5px ์„ค์ • ์ž์ฒด๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, ๋””์ž์ธํŒ€์— ์ˆ˜์ • ์š”์ฒญ

๐Ÿ› queryClient ํ…Œ์ŠคํŠธ ์—๋Ÿฌ

๋ฒ„๊ทธ ๋‚ด์šฉ

๊ธฐ์กด์— generateQueryClient ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋‘๊ณ , ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด QueryClient๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŒ… ํ™˜๊ฒฝ์—์„œ ๋ Œ๋”๋ง์„ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทผ๋ฐ #47 ์—์„œ queryClient๋ฅผ shared๋กœ ์ด๋™ํ•˜๊ณ  ํ…Œ์ŠคํŒ… ํ™˜๊ฒฝ์—์„œ๋„ ๋™์ผํ•œ queryClient๋ฅผ ์ ์šฉํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋ฒ„๊ทธ ์œ„์น˜

  • src/shared/tests/setup.tsx

์žฌํ˜„

ํ• ์ผ

  • ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์›์ธ์„ ์ž์„ธํ•˜๊ฒŒ ๋ถ„์„ํ•œ๋‹ค.
  • ์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

๐Ÿ”ง ๊ณต์šฉ ํƒ€์ž… ์œ„์น˜ ์ด๋™

์ˆ˜์ • ๋ชฉ์ 

shared ๋‚ด๋ถ€์—์„œ ๊ณต์šฉ ํƒ€์ž… ๊ด€๋ฆฌ

์ˆ˜์ • ์ฝ”๋“œ

  • Feed, Comment, User ๋“ฑ์˜ ํƒ€์ž… ์œ„์น˜ ์ด๋™

ํ•  ์ผ ๋ชฉ๋ก

  • ํƒ€์ž… shared๋กœ ์œ„์น˜ ์ด๋™
  • ํ•ด๋‹น ํƒ€์ž…๋“ค์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ํŒŒ์ผ๋“ค์— ๋Œ€ํ•ด import ์ˆ˜์ •

์„ค๋ช…

๊ธฐ์กด์— mocking API์— ์‚ฌ์šฉ๋˜๋Š” ๋ถ€๋ถ„์€ production์— ํฌํ•จ๋˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„์ด๋ผ ์•„์˜ˆ ๋ฐฐ์ œํ•˜๊ณ  ํƒ€์ž…์„ ์ •์˜ํ•˜์˜€์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์ž‘์—…์„ ํ•˜๊ฒŒ ๋˜๋‹ˆ, type์— ๋Œ€ํ•ด ์ค‘๋ณต์ด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์•„์˜ˆ ํƒ€์ž… ์ž์ฒด๋ฅผ shared์— ๋‘๊ณ  mocks์™€ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”ง ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ ๋ฆฌํŒฉํ† ๋ง

์ˆ˜์ • ๋ชฉ์ 

๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ ๋ฆฌํŒฉํ† ๋ง

์ˆ˜์ • ์ฝ”๋“œ

  • src/shared/ui

ํ•  ์ผ ๋ชฉ๋ก

์„ค๋ช…

ํ˜„์žฌ ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ๋กœ์„œ์˜ ์—ญํ• ์„ ์˜จ์ „ํžˆ ์ˆ˜ํ–‰ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

export type ActiveStyle =
  | 'confirm h4semi'
  | 'follow-sm b2semi'
  | 'follow-lg b1semi';

export const ActiveButton = ({
  onClick,
  styleClass,
  isDisabled,
  children,
}: ActiveButtonProps) => {
  const sytleClassname = isDisabled ? styleClass : `${styleClass}-disabled`; // ๐Ÿšจ ํฐํŠธ ๋น„ํ™œ์„ฑํ™”

  return (
    <button
      onClick={onClick}
      className={`${sytleClassname}`}
      disabled={isDisabled}
    >
      {children}
    </button>
  );
};

๋งŒ์ผ ActiveButton์„ ํ™œ์šฉํ•˜๋Š” ๋„์ค‘ ๋ฒ„ํŠผ์ด ๋น„ํ™œ์„ฑํ™”๋˜๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ ํฐํŠธ ํด๋ž˜์Šค๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ํฐํŠธ๊ฐ€ ๋น„ํ™œ์„ฑํ™” ๋˜๋Š” ๋ฌธ์ œ
  • ์‹ ๊ณ ํ•˜๊ธฐ ๋ฒ„ํŠผ์ด ์ƒ‰์ƒ์ด ์œ ํšจํ•˜์ง€ ์•Š์Œ
  • ๋ฒ„ํŠผ์— ๋Œ€ํ•œ ํƒ€์ž…์„ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์—†์Œ
  • onExecute ๋ฉ”์„œ๋“œ์˜ ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•ด์ค˜์•ผํ•จ.

๐Ÿ”ง ํ”ผ๋“œ ์‹ ๊ณ ํ•˜๊ธฐ Props Drilling

์ˆ˜์ • ๋ชฉ์ 

์‹ ๊ณ ํ•˜๊ธฐ API๋ฅผ ํ™œ์šฉํ•˜๊ธฐ ์œ„ํ•ด Props๋กœ ์ „๋‹ฌํ•ด์ฃผ๋Š” ๊ณผ์ •์—์„œ Props Drilling์ด ๋ฐœ์ƒํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•จ.

์ˆ˜์ • ์ฝ”๋“œ

  • src/widgets/feed-main-list/ui/Feed.tsx
  • src/widgets/feed-kebab/ui/FeedKebabButton.tsx
  • src/widgets/feed-kebab/ui/KebabMenu.tsx
  • src/features/feed-reports/ui/FeedReportsForm.tsx
  • src/features/feed-reports/api/useSubmitReports.tsx

ํ•  ์ผ ๋ชฉ๋ก

  • Props Drilling ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ
  • ๋™์‹œ์— ์—ฌ๋Ÿฌ ํ”ผ๋“œ์˜ KebabMenuList๋ฅผ ํŽผ์น  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ

์„ค๋ช…

ํ˜„์žฌ ์‹ ๊ณ ํ•˜๊ธฐ API๋ฅผ ์ ์šฉํ•˜๋Š” ๋ถ€๋ถ„์— ์žˆ์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด props drilling์ด ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Feed -> FeedKebabButton -> KebabMenu -> FeedReportsForm -> useSubmitReports

์œ„์™€ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ์˜ ์—ฐ๊ฒฐ์€ Props Drilling์œผ๋กœ ์ธํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ๊ฒฐํ•ฉ์„ฑ์ด ๊ฐ•ํ•ด์ ธ์„œ, ์ˆ˜์ •์— ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ๊ฐ€ ์ƒ๊ฐํ•œ ๋ฐฉ๋ฒ•์€ Zustand๋ฅผ ์ด์šฉํ•˜์—ฌ feedId์™€ onClose๋ฅผ ์ „์—ญ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค! ์•„์ง ๊ตฌ์ฒด์ ์ธ ๋ฐฉ์•ˆ์— ๋Œ€ํ•ด์„œ ๊ณ„ํšํ•˜์ง€๋Š” ์•Š์•˜์ง€๋งŒ, ํ˜น์‹œ ์ข‹์€ ์˜๊ฒฌ ์žˆ์œผ์‹œ๋‹ค๋ฉด ์˜๊ฒฌ ์ œ์‹œํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

๐Ÿ› iOS factor ์ ์šฉ ์‹œ ๋””์ž์ธ ๊นจ์ง ํ˜„์ƒ

๋ฒ„๊ทธ ๋‚ด์šฉ

iOS Factor๋ฅผ WebView์— ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€์„œ ์ ์šฉํ•˜์˜€๋”๋‹ˆ, Web์—์„œ๋Š” ๋””์ž์ธ์ด ๊นจ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋ฒ„๊ทธ ์œ„์น˜

branch name: fix/issue-35-responsive-design

  • src/app/styles/_fonts.scss
  • src/shared/styles/_box.scss

์žฌํ˜„

iPhone SE 1 ์„ ๊ธฐ์ค€์œผ๋กœ ํ–ˆ์„ ๋•Œ๋Š” ํฐ ์ด์ƒ์ด ์—†์—ˆ์ง€๋งŒ, iPhone SE 3 ๊ทธ ์ด์ƒ์œผ๋กœ height ๊ฐ’์ด ์ปค์ง์— ๋”ฐ๋ผ ๋””์ž์ธ์ด ๊นจ์ง€๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ

๋…ผ์˜ํ•  ์ 

  1. ๋ชจ๋“  ๊ธฐ๊ธฐ์— ๋Œ€ํ•œ factor ๊ฐ’์„ web์— ๋งž์ถฐ ์žฌ ๊ณ„์‚ฐํ•˜์—ฌ ์ ์šฉํ•œ๋‹ค. (ํ”„๋ก ํŠธ๊ฐ€ ๋„ˆ๋ฌด ํž˜๋“ค ์ˆ˜๋„)
  2. ์•„์ดํฐ๊ณผ ์•„์ดํŒจ๋“œ 2๊ฐ€์ง€์— ๋Œ€ํ•ด์„œ๋งŒ ๋””์ž์ธ ์š”์†Œ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜์—ฌ ์ ์šฉํ•œ๋‹ค. (๋””์ž์ด๋„ˆ๊ฐ€ ํž˜๋“ค ์ˆ˜๋„)
  3. ๊ทธ๋ƒฅ ๋ชจ๋“  ๊ธฐ๊ธฐ์— ๋™์ผํ•œ ๋””์ž์ธ์„ ์ œ๊ณตํ•œ๋‹ค. (๋ชจ๋‘๊ฐ€ ํ–‰๋ณตํ•œ ๊ธธ)

๐Ÿ› UI ๊นจ์ง

๋ฒ„๊ทธ ๋‚ด์šฉ

  • Feed Footer์˜ ์•„์ด์ฝ˜์ด ์ˆซ์ž์— ๋”ฐ๋ผ ์›€์ง์ž„
  • Profile Count์˜ ๊ตฌ๋ถ„์„ ์ด ์ˆซ์ž์— ๋”ฐ๋ผ ์›€์ง์ž„

๋ฒ„๊ทธ ์œ„์น˜

  • src/widgets/feed-main-list/ui/Feed.scss
  • src/widgets/profile-user/ui/ProfileCount.scss

์žฌํ˜„

image
image
image
image

ํ• ์ผ

  • PM/๋””์ž์ธํŒ€์— ์ˆซ์ž๊ฐ€ ์ปค์งˆ ๊ฒฝ์šฐ 1์ฒœ, 1๋งŒ or 1K, 1M ๋“ฑ ์ค„์ž„๋ง๋กœ ํ‘œ๊ธฐํ•˜๋„๋ก ์ œ์•ˆ
  • ๋„ˆ๋น„๋ฅผ ์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ •

๐Ÿ› Params URL์— Icon Sprite ๋ฏธ ์ ์šฉ ๋ฒ„๊ทธ

๋ฒ„๊ทธ ๋‚ด์šฉ

Params๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š๋Š” URL์—์„œ๋Š” ์ •์ƒ์ ์œผ๋กœ Icon Sprite๊ฐ€ ์ ์šฉ๋˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Params๊ฐ€ ํฌํ•จ๋œ URL(feeds/:feedId) ๋“ฑ์—์„œ๋Š” Icon์ด ์ •์ƒ์ ์œผ๋กœ ์ ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋ฒ„๊ทธ ์œ„์น˜

  • Icon ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์žฅ์†Œ

์žฌํ˜„

  • Params URL์„ router์— ๋“ฑ๋กํ•˜๊ณ , ํ•ด๋‹น ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด svg๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image

ํ• ์ผ

  • ๋ฒ„๊ทธ ์›์ธ ๋ถ„์„
  • ๋ฒ„๊ทธ ์ˆ˜์ •

โœจ ์•Œ๋ฆผ ์กฐํšŒ, ํŒ”๋กœ์šฐ ์š”์ฒญ ์ˆ˜๋ฝ / ๊ฑฐ์ ˆ ๊ฐœ๋ฐœ ๊ฒ€ํ† 

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

์•Œ๋ฆผ ์กฐํšŒ, ํŒ”๋กœ์šฐ ์š”์ฒญ ์ˆ˜๋ฝ / ๊ฑฐ์ ˆ

ํ•  ์ผ ๋ชฉ๋ก

์•„๋ž˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ์‹œ, ์•Œ๋ฆผ์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ด๋ค„์ ธ์•ผ ํ•˜๋Š”์ง€ ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • ์ž‘์„ฑํ•œ ๊ธ€์— ์ข‹์•„์š”๋ฅผ ๋ˆŒ๋ €์„ ์‹œ
  • ์ž‘์„ฑํ•œ ๊ธ€์— ๋Œ“๊ธ€์ด ์ž‘์„ฑ๋˜์—ˆ์„ ์‹œ
  • ํŒ”๋กœ์šฐ ์š”์ฒญ์ด ์ผ์–ด๋‚ฌ์„ ์‹œ
  • ํŒ”๋กœ์šฐ ์ˆ˜๋ฝ/๊ฑฐ์ ˆ์ด ์ผ์–ด๋‚ฌ์„ ์‹œ

์„ค๋ช…

ํ˜„์žฌ FooterNavbar๋ฅผ ์ œ์™ธํ•œ Feed page์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์šฐ์„  WebView๋กœ ๊ฐœ๋ฐœํ•˜๊ธฐ๋กœ ํ•ฉ์˜ํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

๊ทธ ์ค‘ ์•Œ๋ฆผ ์กฐํšŒ, ํŒ”๋กœ์šฐ ์š”์ฒญ ์ˆ˜๋ฝ / ๊ฑฐ์ ˆ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
WebView์—์„œ ์ด ๊ธฐ๋Šฅ๋“ค์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผ ํ• ์ง€์— ๋Œ€ํ•œ ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”ง FSD Architecture Public API ์ ์šฉ

์ˆ˜์ • ๋ชฉ์ 

ํ˜„์žฌ Public API๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๋ถ€๋ถ„์— ๋Œ€ํ•ด Public API ์ ์šฉ

์ˆ˜์ • ์ฝ”๋“œ

  • ๋Œ€๋ถ€๋ถ„์˜ ํด๋”..

ํ•  ์ผ ๋ชฉ๋ก

  • Slice ๊ณ„์ธต์— ๋Œ€ํ•ด Public API ์ ์šฉ
  • Layer ๊ณ„์ธต์— ๋Œ€ํ•ด Public API ์ œ๊ฑฐ

์„ค๋ช…

FSD Architecture๊ฐ€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด๋ฉด, Public API๋ฅผ ํ†ตํ•ด ์บก์Šํ™”๋ฅผ ๋‹ฌ์„ฑํ•œ๋‹ค๊ณ  ๋‚˜์™€์žˆ๋Š” ๋งŒํผ Public API์€ ํ•ด๋‹น ์•„ํ‚คํ…์ฒ˜์—์„œ ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ ๋ฒ ์ด์Šค๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ํด๋”์—์„œ Public API๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๊ณ  ์žˆ์–ด์„œ ์ด๋ฅผ ์ˆ˜์ •ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

โœจ ProfileFeed ์ œ๊ฑฐ

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

  • ProfileFeed Interface ์ œ๊ฑฐ

ํ•  ์ผ ๋ชฉ๋ก

  • ProfileFeed ์ œ๊ฑฐ
  • ProfileFeed Interface๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ ์ˆ˜์ •

์„ค๋ช…

  • UI๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉฐ Profile์˜ Feed์™€ MainFeed๊ฐ€ ๋™์ผํ•œ ์‚ฌํ•ญ์„ ์ถœ๋ ฅํ•˜๋„๋ก ์ˆ˜์ •๋˜์—ˆ๊ธฐ์—, ์ด์— ๋”ฐ๋ผ Profile Page์—์„œ Feed๋ฅผ ์ถœ๋ ฅํ• ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ProfileFeed ํ•ญ๋ชฉ ์ œ๊ฑฐ

๐Ÿ”ง ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ๊ณ ๋„ํ™” ์ž‘์—…

์ˆ˜์ • ๋ชฉ์ 

  • Lighthouse ๊ธฐ์ค€ Performance Score ๊ฐœ์„ 

ํ•  ์ผ ๋ชฉ๋ก

  • #88
  • #90
  • ์ด๋ฏธ์ง€ CloudFront ์ ์šฉ
  • ์ด๋ฏธ์ง€ webp ์ ์šฉ

์„ค๋ช…

ํ˜„์žฌ ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€, Lighthouse ๊ธฐ์ค€ ์„ฑ๋Šฅ ์ ์ˆ˜๊ฐ€ 55์ ์ด ๋‚˜์˜ค๊ณ  ์žˆ์–ด 80์  ์ด์ƒ์„ ๋ชฉํ‘œ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ”งํ˜„์‹ค์ ์ธ ํ™˜๊ฒฝ ๊ตฌ์ถ•์„ ์œ„ํ•ด ๋ชจ๋“  API์— ๋Œ€ํ•ด delay ์„ค์ •

์ˆ˜์ • ๋ชฉ์ 

ํ˜„์žฌ ํ”ผ๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๊ธฐ ์œ„ํ•œ API์—๋งŒ delay๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ์–ด, ๋ชจ๋“  API์— ๋Œ€ํ•ด delay๋ฅผ ์ ์šฉํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.

์ˆ˜์ • ์ฝ”๋“œ

  • src/app/mocks/handler

ํ•  ์ผ ๋ชฉ๋ก

  • Mocking Service Worker์— ๋“ฑ๋ก๋œ ๋ชจ๋“  API์— ๋Œ€ํ•œ ์ง€์—ฐ ์ฒ˜๋ฆฌ

์„ค๋ช…

์ฆ‰๊ฐ์ ์ธ ์‘๋‹ต์ด ์˜ค๋ฉด ๊ฐœ๋ฐœ ์†๋„๋Š” ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์œผ๋‚˜, ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋ฏธํกํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด์— ํ˜„์‹ค์ ์ธ ํ™˜๊ฒฝ ๊ตฌ์ถ•์„ ํ†ตํ•ด API ์‘๋‹ต ์ƒํ™ฉ์— ๋”ฐ๋ฅธ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Mocking Service Worker์— ๋“ฑ๋ก๋œ ๋ชจ๋“  API์— ๋Œ€ํ•ด delay๋ฅผ ์„ค์ •ํ•ด ์ฃผ์–ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

โœจ ํŽ˜์ด์ง€ ์ „ํ™˜ Animation ์ถ”๊ฐ€

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

  • ํŽ˜์ด์ง€ ์ „ํ™˜ ์‹œ Animation ์ถ”๊ฐ€

ํ•  ์ผ ๋ชฉ๋ก

  • ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ „ํ™˜ ์‹œ ์ขŒ์ธก์—์„œ ํ™”๋ฉด์ด ์Šฌ๋ผ์ด๋”ฉํ•ด ํ˜„์žฌ ํ™”๋ฉด์„ ๋ฎ๋„๋ก
  • ์ด ์™ธ ๋ฒ„ํŠผ์œผ๋กœ ์ „ํ™˜ ์‹œ ์šฐ์ธก์—์„œ ํ™”๋ฉด์ด ์Šฌ๋ผ์ด๋”ฉํ•ด ํ˜„์žฌ ํ™”๋ฉด์„ ๋ฎ๋„๋ก

์„ค๋ช…

iOS App์—์„œ ํ™”๋ฉด ์ „ํ™˜ ์‹œ ํ•ด๋‹น Animation์œผ๋กœ ํ™”๋ฉด์ด ์ „ํ™˜๋˜๊ณ  ์žˆ๊ธฐ์—, ํ†ต์ผ๊ฐ์„ ์ฃผ๊ธฐ ์œ„ํ•ด์„œ ๊ตฌํ˜„

โšก ์ด๋ฏธ์ง€ lazy loading ์ฒ˜๋ฆฌ

ํ˜„์žฌ ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ์žˆ์–ด์„œ, ์ด๋ฏธ์ง€ ์ง€์—ฐ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋Š” ๋ Œ๋”๋ง
  • ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ถ€ํ„ฐ ์ง€์—ฐ ์ฒ˜๋ฆฌ ์ ์šฉ

๐Ÿ› ๋„คํŠธ์›Œํฌ ๋ถˆ์•ˆ์ • ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๊ด€๋ฆฌ

๋ฒ„๊ทธ ๋‚ด์šฉ

ํ˜„์žฌ ๋„คํŠธ์›Œํฌ ๋ถˆ์•ˆ์ • ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ชจ๋“  API์— ๋Œ€ํ•ด ์ ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด ์•„๋‹Œ, ๋ฌดํ•œ ์Šคํฌ๋กค ๊ด€๋ จ ์—๋Ÿฌ์—์„œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ์— ๋‹ค๋ฅธ API๋“ค์— ๋Œ€ํ•ด ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ์—๋Š” ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ ํ† ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ฒ„๊ทธ ์œ„์น˜

  • src/widgets/feed-main-list/ui/FeedMainList.tsx
  • src/shared/ui/network-error-toast

์žฌํ˜„

ํ• ์ผ

  • ์—๋Ÿฌ ์ค‘์•™ํ™” ๊ด€๋ฆฌ
  • ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ ํŽธ์˜์„ฑ ํ–ฅ์ƒ

๐Ÿ› fonts ๋™์ž‘ ์˜ค๋ฅ˜

๋ฒ„๊ทธ ๋‚ด์šฉ

image

  • ์„ค์ •๋œ font๊ฐ€ ์˜๋„ํ•œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์Œ
    • font-weight ์ค‘ bold๋ฅผ ์ œ์™ธํ•œ medium, regular, semi-bold๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์Œ
    • letter-spacing ์—ญ์‹œ %๋กœ ์ ์šฉ๋˜์ง€ ์•Š์Œ
  • font์˜ size์— ๋ฐ˜์‘ํ˜•์ด ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š์Œ

๋ฒ„๊ทธ ์œ„์น˜

  • shared/styles/fonts.scss

ํ• ์ผ

  • font-weight์ด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก
  • letter-spacing์ด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก
  • font size๊ฐ€ ๊ธฐ๊ธฐ ์‚ฌ์ด์ฆˆ์— ๋”ฐ๋ผ ๋ณ€๋™๋˜๋„๋ก
  • ํฐํŠธ ํด๋ž˜์Šค๊ฐ€ ์ค‘๋ณต์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ - #29

โœจ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง ๋ฐ ๊ตฌํ˜„

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

  • ํ”ผ๋“œ ์ˆจ๊น€์— ๋Œ€ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
  • ํ”ผ๋“œ ์‹ ๊ณ ์— ๋Œ€ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
  • ๊ธฐ์กด ํ…Œ์ŠคํŠธ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง

ํ•  ์ผ ๋ชฉ๋ก

  • ํ”ผ๋“œ ์ˆจ๊ธฐ๊ธฐ ๋ฐ ์‹ ๊ณ  ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
  • ํ”ผ๋“œ ์‹ ๊ณ  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
  • ๊ธฐ์กด ํ…Œ์ŠคํŠธ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง

์„ค๋ช…

๊ธฐ์กด์— ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์ธ์ง€, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์ธ์ง€์— ๋Œ€ํ•ด ์• ๋งค๋ชจํ˜ธํ•œ ๋ถ€๋ถ„์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ๊ฒฝ๊ณ„๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ› ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์‹œ์—ฐ ์ค‘ ์ด์Šˆ

๋ฒ„๊ทธ ๋‚ด์šฉ

  1. ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ์Šคํฌ๋กค ์‹œ ์šฐ์ธก์— ์Šคํฌ๋กค์ด ํ‘œ์‹œ๋˜๋Š” ๋ฌธ์ œ
  2. ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ์Šคํฌ๋กค ์‹œ 2 ํŽ˜์ด์ง€๋ถ€ํ„ฐ ์Šค์ผˆ๋ ˆํ†ค UI๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ
  3. ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค ์ถ”๊ฐ€ (1 ํŽ˜์ด์ง€ ์„ฑ๊ณต -> 2 ํŽ˜์ด์ง€ ์‹คํŒจ -> 2 ํŽ˜์ด์ง€ ์„ฑ๊ณต)

๋ฒ„๊ทธ ์œ„์น˜

  • src/widgets/feed-main-list/ui/FeedMainList.tsx

์žฌํ˜„

  1. ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ํ•˜๋‹จ์œผ๋กœ ์Šคํฌ๋กค
  2. ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ ํ•˜๋‹จ์œผ๋กœ ์Šคํฌ๋กค
  3. ํ”ผ๋“œ ๋ฆฌ์ŠคํŠธ ์š”์ฒญ์— ๋Œ€ํ•œ ์„œ๋ฒ„ ์‘๋‹ต ์‹œ๊ฐ„ delay๋ฅผ 2~4์ดˆ๋กœ ๋žœ๋ค์œผ๋กœ ์„ค์ •ํ•˜๊ณ , timeout์„ 3์ดˆ๋กœ ์„ค์ •ํ•˜์—ฌ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ™•์ธํ•  ์˜ˆ์ •

ํ• ์ผ

  • ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์Šคํฌ๋กค ์ œ๊ฑฐ
  • ํ”ผ๋“œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์Šคํฌ๋กค ์‹œ 2ํŽ˜์ด์ง€ ์ดํ›„์—๋„ ์Šค์ผˆ๋ ˆํ†ค UI ํ‘œ์‹œ
  • ํ”ผ๋“œ ๋ฆฌ์ŠคํŠธ ์š”์ฒญ ๋žœ๋ค ์กฐ์ • ๋ฐ ์‹œ๋‚˜๋ฆฌ์˜ค ํ™•์ธ

๐Ÿ”ง ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•œ spriteMap ๋ถ„๋ฆฌ

์ˆ˜์ • ๋ชฉ์ 

  • ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•œ spriteMap ๋ถ„๋ฆฌ

์ˆ˜์ • ์ฝ”๋“œ

  • src/shared/ui/icon/consts/sprite.ts
  • src/shared/ui/icon/ui/Icon.tsx

ํ•  ์ผ ๋ชฉ๋ก

  • spriteMap์„ ํŠน์ • ํ–‰์œ„ ํ˜น์€ ๋ชฉ์ ์— ๋”ฐ๋ผ ๋ถ„๋ฆฌ
  • ๋ถ„๋ฆฌ๋œ spriteMap์˜ ํƒ€์ž…์„ ์œ ์ถ”ํ•˜์—ฌ IconComponent์— ์ ์šฉ

์„ค๋ช…

export type IconName =
  | 'back'
  | 'notification'
  | 'share'
  | 'kebab-menu'
  | 'writing'
  | 'bookmark'
  | 'pennyway-logo'
  | 'like'
  | 'chat'
  | 'search'
  | 'caution'
  | 'no-profile'
  | 'checkbox-circle_off'
  | 'checkbox-circle_on'
  | 'checkbox-square_on'
  | 'checkbox-square_off';
  • ์•„์ด์ฝ˜์˜ ์ˆ˜๊ฐ€ ๋งŽ์•„์ง์— ๋”ฐ๋ผ ๊ด€๋ฆฌ์˜ ๋ณต์žก์„ฑ โ†‘ ๋ฐ ๊ฐ€๋…์„ฑ โ†“

โœจ User content ์ œ๊ฑฐ

์ถ”๊ฐ€ํ•  ๊ธฐ๋Šฅ

  • User Interface ๋‚ด content ์ œ๊ฑฐ

ํ•  ์ผ ๋ชฉ๋ก

  • User Interface ๋‚ด content ์ œ๊ฑฐ
  • User Interface๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ ์ˆ˜์ •

์„ค๋ช…

  • UI๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉฐ Webview์—์„œ User๊ฐ€ ์ƒํƒœ๋ฉ”์„ธ์ง€๋ฅผ ์ถœ๋ ฅํ•˜์ง€ ์•Š๋„๋ก ์ˆ˜์ •๋˜์—ˆ๊ธฐ์—, ์ด์— ๋”ฐ๋ผ User Interface์—์„œ ์ƒํƒœ๋ฉ”์„ธ์ง€๋ฅผ ๋‹ด๋‹นํ•˜๋Š” content ํ•ญ๋ชฉ ์ œ๊ฑฐ

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.