관심사 분리와 폴더구조 39일차

anvel·2025년 4월 29일

항해 플러스

목록 보기
21/39

항해 플러스 프론트엔드 - 관심사 분리과 폴더구조

관심사 분리 및 폴더 구조

어제까지해서 관심사를 모두 분리하고 남은 page 코드입니다. props를 완전히 없앴으며, view만 있는 item 정도만 받아서 수행하는 것으로 변경했습니다.

import { PostsHeader } from "@/features/posts/ui/PostsHeader";
import { PostsPagination } from "@/features/posts/ui/PostsPagination";
import { PostsSearchHeader } from "@/features/posts/ui/PostsSearchHeader";
import { PostsTable } from "@/features/posts/ui/PostsTable";
import { PostsDialogs } from "@/widgets/posts/ui/PostsDialogs";
import { Card, CardContent, CardHeader } from "../shared/ui";

const PostsManager = () => {
  return (
    <Card className="w-full max-w-6xl mx-auto">
      <CardHeader>
        <PostsHeader />
      </CardHeader>
      <CardContent>
        <div className="flex flex-col gap-4">
          <PostsSearchHeader />
          <PostsTable />
          <PostsPagination />
        </div>
      </CardContent>
      <PostsDialogs />
    </Card>
  );
};

export default PostsManager;

API 분절과 조합

회사에서 자주 사용하던 패턴인데, URL을 구성하는 Path들을 모두 상수 단어화 하여 사용하는 방식으로 변경해봤습니다. 이렇게 만들었던 이유는 정해진 단어에 대하여 확실하게 오타를 배제하고 사용할 수 있도록 할 수 있기 때문이었습니다.

import { Comment } from "@/entities/comment/models/comment.types";
import { Post } from "@/entities/post/models";
import { User } from "@/entities/user/models/user.types";

// SEGMENTS
const _API = "api";
const _ADD = "add";
const _POSTS = "posts";
const _COMMENTS = "comments";
const _USERS = "users";
const _TAG = "tag";
const _TAGS = "tags";
const _SEARCH = "search";
const _POST = "post";

// URL
export const API_URL = {
  // posts
  POSTS: (limit: number, skip: number) => `/${_API}/${_POSTS}?limit=${limit}&skip=${skip}` as const,
  POSTS_ID: (id: Post["id"]) => `/${_API}/${_POSTS}/${id}` as const,
  POSTS_ADD: `/${_API}/${_POSTS}/${_ADD}`,
  POSTS_TAG: (tag: Post["tags"][number]) => `/${_API}/${_POSTS}/${_TAG}/${tag}` as const,
  POSTS_TAGS: `/${_API}/${_POSTS}/${_TAGS}`,
  POSTS_SEARCH: (query: string) => `/${_API}/${_POSTS}/${_SEARCH}?q=${query}` as const,

  // comments
  COMMENTS_ID: (id: Comment["id"]) => `/${_API}/${_COMMENTS}/${id}` as const,
  COMMENTS_ADD: `/${_API}/${_COMMENTS}/${_ADD}`,
  COMMENTS_POST_ID: (id: Post["id"]) => `/${_API}/${_COMMENTS}/${_POST}/${id}` as const,

  // users
  USERS: `/${_API}/${_USERS}?limit=0&select=username,image`,
  USERS_ID: (id: User["id"]) => `/${_API}/${_USERS}/${id}` as const,
} as const;

사용하는 쪽은 URL을 함수처럼 사용하도록 typescript 를 통해 const로 선언하여 고정된 문자열이 반환됨을 확정하여 오류가 없도록 구현하였습니다.

import { API_URL } from "@/shared/lib/api-path";
import { Post, PostsData, UsersData } from "../models";

export const fetchPostsAPI = async (limit: number, skip: number) => {
  const response = await fetch(API_URL.POSTS(limit, skip));
  const postsData = (await response.json()) as PostsData;

  const response2 = await fetch(API_URL.USERS);
  const usersData = (await response2.json()) as UsersData;

  const posts: Post[] = postsData.posts.map((post) => {
    const author = usersData.users.find((user) => user.id === post.userId);
    if (!author) throw new Error("wrong author");
    return { ...post, author };
  });

  return { posts, total: postsData.total };
};

TODO

아직 파일을 분리하고, 함수와 API를 나누는 정도의 작업이 완료된 상태인데, 현재까지만 하고 심화과제를 진행하는 방향으로 진행할 것 같습니다.

0개의 댓글