Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions apps/web/entities/post/model/api.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entities 슬라이스에, model 세그먼트에 아래코드를 작성하셨는지 이유가 있을까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dgd03146 님 리뷰 감사합니다!😊

@rmkim7 님과 논의하여 해당 파일을 배치하였는데 처음에는 해당 코드를 단순하게 데이터를 다루는 요소의 일부라는 저의 생각 하에 model에 배치했던 것 같습니다.

리뷰를 통해 다시 논의해본 결과, API는 데이터를 가져오는 역할이고, 모델은 데이터를 가공하고 사용하는 역할이라고 의견을 모았습니다. 예를 들어, PostViewModel 같은 파일을 만든다면 model 폴더에 두겠지만, API는 api 폴더에 있는 게 더 명확해 보일 거란 의견을 주고 받았습니다.

따라서 데이터 소스 역할을 하는 해당 파일은 entities/post/api/에 두고, 파일명 또한 post-api.ts 로 변경할 예정입니다. 이 부분은 빠른 시일 내에 업데이트하도록 하겠습니다.

좋은 관점 제공해주셔서 감사합니다!🙂

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { httpClient } from '@/shared/api/http-client';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FSD에서는 index.ts를 통해서만 외부로 공개하는 걸 원칙으로 두고 있는데 세그먼트에서 코드를 직접적으로 가져오는 것은 캡슐화 원칙을 깨는 것 아닐까 하는 생각이 듭니다. Shared에서는 segment 하나당 별도의 공개 API를 적용하는게 추천된다고 하네요.
https://feature-sliced.design/kr/docs/get-started/tutorial#%EC%97%84%EA%B2%A9%ED%95%9C-%EA%B3%B5%EA%B0%9C-api-%EC%A0%95%EC%9D%98

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선은 팀원인 선오님과 배럴 파일을 따로 생성할지 말지 논의하다가
어떤 게 더 나을지 비교해보려고 선오님은 배럴 파일을 생성하고
저는 생성하지 않기로 해서 해당 파일의 경로를 직접 지정했습니다.
추후에 비교해보고 하나를 정할 예정인데 그때 거정님이 말씀하신 부분 참고하겠습니다.
조언 감사합니다!

import { CreatePostDTO, Post, UpdatePostDTO } from '@/shared/types/post-types';

// 게시글 생성 (POST /posts)
export const createPost = async (postData: CreatePostDTO): Promise<Post> => {
return httpClient<Post>('/posts', {
method: 'POST',
body: JSON.stringify(postData),
});
};

// 모든 게시글 조회 (GET /posts)
export const getPosts = async (): Promise<Post[]> => {
return httpClient<Post[]>('/posts');
};

// 특정 게시글 조회 (GET /posts/{id})
export const getPostById = async (postId: string): Promise<Post> => {
return httpClient<Post>(`/posts/${postId}`);
};

// 게시글 수정 (PUT /posts/{id})
export const updatePost = async (
postId: string,
updatedData: UpdatePostDTO
): Promise<Post> => {
return httpClient<Post>(`/posts/${postId}`, {
method: 'PUT',
body: JSON.stringify(updatedData),
});
};

// 게시글 삭제 (DELETE /posts/{id})
export const deletePost = async (
postId: string
): Promise<{ message: string }> => {
return httpClient<{ message: string }>(`/posts/${postId}`, {
method: 'DELETE',
});
};
19 changes: 19 additions & 0 deletions apps/web/shared/api/http-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const BASE_URL = 'http://localhost:4000/api';

export const httpClient = async <T>(
endpoint: string,
options?: RequestInit
): Promise<T> => {
const response = await fetch(`${BASE_URL}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
},
...options,
});

if (!response.ok) {
throw new Error(`API 요청 실패: ${response.status}`);
}

return response.json();
};
26 changes: 26 additions & 0 deletions apps/web/shared/types/post-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export interface Post {
id: string;
title: string;
content: string;
author: string;
createdAt: string;
updatedAt?: string;
}

export type CreatePostDTO = Pick<Post, 'title' | 'content' | 'author'>;
export type UpdatePostDTO = Partial<CreatePostDTO>;

interface CursorPaginationResponse<T> {
data: T[];
nextCursor: string | null;
hasMore: boolean;
}
interface OffsetPaginationResponse<T> {
data: T[];
currentPage: number;
totalPages: number;
hasMore: boolean;
}
Comment on lines +13 to +23
Copy link
Collaborator

@choi1five choi1five Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CursorPaginationResponseOffsetPaginationResponse 인터페이스가 잘 정의되어 있네요!

혹시 post-types에 위치하기보다는 더 범용적으로 사용될 수 있는 타입이어서 별도의 파일로 분리해보는 건 어떨까요?
이렇게 하면 다른 모듈에서도 페이지네이션 인터페이스를 쉽게 재사용할 수 있을 것 같습니다.

Copy link
Contributor Author

@rmkim7 rmkim7 Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 게시글 기능을 기준으로 작성했습니다.
추후에 댓글 기능과 병합할 때 폴더 구조, 파일명 등을 댓글 팀과 논의한 뒤 필요하다면 별도의 파일로 분리하는 게 좋을 것 같습니다.
좋은 의견 감사합니다:)


export type GetPostsCursor = CursorPaginationResponse<Post>;
export type GetPostsOffset = OffsetPaginationResponse<Post>;