Skip to content

React 정미주 sprint5 제출합니다#74

Draft
040l28 wants to merge 4 commits intocodeit-sprint-fullstack:react-정미주from
040l28:react-정미주-sprint5

Hidden character warning

The head ref may contain hidden characters: "react-\uc815\ubbf8\uc8fc-sprint5"
Draft

React 정미주 sprint5 제출합니다#74
040l28 wants to merge 4 commits intocodeit-sprint-fullstack:react-정미주from
040l28:react-정미주-sprint5

Conversation

@040l28
Copy link
Collaborator

@040l28 040l28 commented Jan 18, 2026

기본 요구사항

공통

  • Github에 스프린트 미션 PR을 만들어 주세요.
  • React, Express를 사용해 진행합니다.

프론트엔드 구현 요구사항

랜딩 페이지

  • HTML과 CSS로 구현한 랜딩페이지를 React로 마이그레이션하세요.
  • 랜딩 페이지 url path는 "/"로 설정하세요.

중고마켓 페이지

  • 중고마켓 페이지 url path를 "/items"으로 설정하세요.
  • 페이지 주소가 "/items" 일 때 상단내비게이션바의 "중고마켓" 버튼의 색상은 "3692FF"입니다.
  • 중고마켓 페이지 판매 중인 상품은 본인이 만든 GET 메서드를 사용해 주세요.
  • 다만 좋아요 순 정렬 기능은 제외해 주세요.
  • 사진은 디폴트 이미지로 프론트엔드에서 처리해주세요.
  • 베스트 상품 목록 조회는 구현하지 않습니다.
  • '상품 등록하기' 버튼을 누르면 "/registration" 로 이동합니다. ( 빈 페이지 )

상품 등록 페이지

  • PC, Tablet, Mobile 디자인에 해당하는 상품 등록 페이지를 만들어 주세요.
  • 상품 등록 url path는 "/registration"입니다.
  • 상품 등록은 본인이 만든 POST 메서드를 사용해 주세요.
  • 등록 성공 시, 해당 상품 상세 페이지로 이동합니다. (빈페이지)

백엔드 구현 요구사항

중고마켓

  • Product 스키마를 작성해 주세요.

  • id, name, description, price, tags, createdAt, updatedAt필드를 가집니다.

  • 필요한 필드가 있다면 자유롭게 추가해 주세요.

  • 상품 등록 API를 만들어 주세요.

  • name, description, price, tags를 입력하여 상품을 등록합니다.

  • 상품 상세 조회 API를 만들어 주세요.

  • id, name, description, price, tags, createdAt를 조회합니다.

  • 상품 수정 API를 만들어 주세요.

  • PATCH 메서드를 사용해 주세요.

  • 상품 삭제 API를 만들어 주세요.

  • 상품 목록 조회 API를 만들어 주세요.

  • id, name, price, createdAt를 조회합니다.

  • offset 방식의 페이지네이션 기능을 포함해 주세요.

  • 최신순(recent)으로 정렬할 수 있습니다.

  • name, description에 포함된 단어로 검색할 수 있습니다.

  • 각 API에 적절한 에러 처리를 해 주세요.

  • 각 API 응답에 적절한 상태 코드를 리턴하도록 해 주세요.

  • . env 파일에 환경 변수를 설정해 주세요.

  • CORS를 설정해 주세요.

  • render.com로 배포해 주세요.

  • MongoDB를 활용해 주세요.

멘토님께

시간이 부족해서 미완성으로 제출합니다.
폴더 구조랑 컴포넌트 분리 기준이 헷갈려서 아키텍처 방향 설명 주시면 감사하겠습니다.

@040l28 040l28 marked this pull request as draft January 18, 2026 12:35
@040l28 040l28 self-assigned this Jan 18, 2026
@040l28 040l28 added the 미완성 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다. label Jan 18, 2026
@040l28 040l28 requested a review from puretension January 18, 2026 12:37
Copy link
Collaborator

@puretension puretension left a comment

Choose a reason for hiding this comment

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

안녕하세요 미주님! 미완성 제출이라고 하셨지만 사실 시간만 쪼개서 내면 완성이 가능한 과제니, 스트레스는 크게 안받으셨으면 합니다. 질문주신대로, 핵심 인사이트가 될만한 부분들(폴더, 파일 구조) 위주로 피드백드리고, 가장 중요한 풀스택에서 데이터 플로우를 이해할 수 있도록 피드백드리겠습니다.

현재 구현 상태 및 핵심 학습 목표

보시다시피 Express와 MongoDB를 사용한 백엔드 구조가 기본적으로 갖춰져 있습니다. 전체적인 구조도 중요하지만 이번 과제에서 얻어가셔야할 건 API Server(Express로 구현된 백엔드)가 DB에서 데이터를 꺼내서 클라이언트(React로 구현된 프론트엔드)에게 주는 플로우를 이해하는 것이 메인입니다.

현재 프로젝트는 프론트엔드(React)와 백엔드(Express)가 하나의 프로젝트에 공존하는 모노레포 형태로, 이런 구조는 소규모 프로젝트에서는 관리가 편할 수 있지만 확장성과 배포 측면에서 고려할 점이 있습니다.

데이터 플로우 이해하기

먼저 가장 중요한 데이터 플로우를 이해해보겠습니다. 이 데이터 플로우를 이해하는 것이 가장!! 중요합니다. 이는 시간이 지나 풀스택 개발시 백엔드 서버 배포까지 해야하는데, 이번 과제가 전형적인 3-tier 아키텍처 구현을 위한 첫걸음이기 때문입니다. 백엔드 코드를 과제 조건에 맞게 정갈하게 작성하는 것도 중요하지만, 제가 아래에서 강조하는 부분들은 한번 이해해놓으시면 앞으로 어플리케이션 레이어 개발을 개발을 하실 때 평생 도움이 되실 거라 자부합니다.

[React 컴포넌트] → [API 호출] → [Express 라우터] → [MongoDB] → [응답] → [React 상태 업데이트]

실제 예시로 보는 플로우:

  1. 프론트엔드에서 API 호출
// src/api/productsApi.js
const response = await fetch('/api/products');
const data = await response.json();
  1. 백엔드에서 요청 받기
// src/routes/posts.js
postRouter.get('/', async (req, res, next) => {
  const posts = await Post.find();  // MongoDB에서 데이터 조회
  res.json({ success: true, data: posts });  // 클라이언트에 응답
});
  1. 프론트엔드에서 데이터 사용
// React 컴포넌트에서
const [products, setProducts] = useState([]);
useEffect(() => {
  fetchProducts().then(setProducts);
}, []);

이 플로우가 핵심이고, 이를 더 체계적으로 관리하기 위해 폴더 구조를 개편해보겠습니다.

폴더 구조 및 아키텍처 방향

현재 구조 분석

현재 구조:
src/
├── server.js              # 백엔드 진입점
├── routes/                 # API 라우트
├── models/                 # 데이터베이스 모델
├── middlewares/            # 백엔드 미들웨어
├── errors/                 # 에러 클래스들
├── pages/                  # React 페이지 컴포넌트
├── components/             # React 컴포넌트
├── hooks/                  # React 커스텀 훅
├── api/                    # 프론트엔드 API 호출
└── assets/                 # 정적 자원

권장하는 개편 구조

백엔드와 프론트엔드를 명확히 분리하여 데이터 플로우를 더 잘 이해할 수 있도록 구성합니다.

프로젝트 루트/
├── backend/                # 백엔드 전용 폴더
│   ├── server.js          # Express 서버 진입점
│   ├── routes/            # API 엔드포인트 정의
│   │   ├── posts.js       # POST /api/posts, GET /api/posts
│   │   └── products.js    # 상품 관련 API
│   ├── models/            # MongoDB 스키마
│   │   ├── Post.js        # 게시글 데이터 구조
│   │   └── Product.js     # 상품 데이터 구조
│   ├── controllers/       # 비즈니스 로직 처리
│   │   ├── postController.js
│   │   └── productController.js
│   ├── middlewares/       # 요청 전처리
│   │   ├── cors.js
│   │   ├── errorHandler.js
│   │   └── validation.js
│   └── config/            # 설정 파일
│       └── database.js    # MongoDB 연결 설정
└── frontend/              # 프론트엔드 전용 폴더
    ├── src/
    │   ├── pages/         # 페이지 컴포넌트
    │   ├── components/    # 재사용 컴포넌트
    │   ├── hooks/         # 커스텀 훅
    │   ├── services/      # API 호출 로직
    │   │   ├── postApi.js     # 백엔드 /api/posts 호출
    │   │   └── productApi.js  # 백엔드 /api/products 호출
    │   └── utils/         # 유틸리티 함수
    └── public/

데이터 플로우 개선 예시

현재 posts.js를 개선하여 더 명확한 플로우를 만들어보겠습니다:

// backend/controllers/postController.js
export class PostController {
  // 1. 클라이언트 요청을 받아서
  async getPosts(req, res, next) {
    try {
      const { page = 1, limit = 10 } = req.query;
      
      // 2. 데이터베이스에서 데이터를 조회하고
      const posts = await Post.find()
        .limit(limit * 1)
        .skip((page - 1) * limit)
        .sort({ createdAt: -1 });
      
      const total = await Post.countDocuments();
      
      // 3. 클라이언트에게 응답을 보냄
      res.json({
        success: true,
        data: posts,
        pagination: {
          page: Number(page),
          limit: Number(limit),
          total,
          pages: Math.ceil(total / limit)
        }
      });
    } catch (error) {
      next(error);
    }
  }

  async createPost(req, res, next) {
    try {
      // 1. 클라이언트에서 받은 데이터로
      const { name, description, price, tags } = req.body;
      
      // 2. 새로운 게시글을 데이터베이스에 저장하고
      const newPost = new Post({ name, description, price, tags });
      await newPost.save();
      
      // 3. 생성된 게시글을 클라이언트에게 응답
      res.status(201).json({
        success: true,
        data: newPost,
        message: '게시글이 생성되었습니다.'
      });
    } catch (error) {
      next(error);
    }
  }
}
// frontend/src/services/postApi.js
class PostApi {
  // 백엔드 API와 통신하는 전용 클래스
  async getPosts(page = 1, limit = 10) {
    const response = await fetch(`/api/posts?page=${page}&limit=${limit}`);
    return await response.json();
  }

  async createPost(postData) {
    const response = await fetch('/api/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(postData)
    });
    return await response.json();
  }
}

export const postApi = new PostApi();
// frontend/src/hooks/usePosts.js
import { useState, useEffect } from 'react';
import { postApi } from '../services/postApi';

export function usePosts() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        setLoading(true);
        const result = await postApi.getPosts();  // 백엔드 API 호출
        setPosts(result.data);  // 받은 데이터로 상태 업데이트
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchPosts();
  }, []);

  return { posts, loading, error };
}

핵심 인사이트

데이터가 흘러가는 양상을 완전히 이해하는 것이 이번 과제의 핵심인 거 같습니다. 폴더/파일 구조, 아키택처 패턴을 알려드리는 것도 중요하지만, 이 데이터 플로우를 이해하기만하면, 사실 저런 것들은 알아서 따라오는거 같아요. 스스로 욕심이 생기고 더 가속도가 붙어 혼자서도 익힐 수 있는 영역이라고 생각합니다. 데이터가 MongoDB에서 시작해서 Express Controller를 거쳐 API Response로 변환되고, React Hook에서 받아서 Component State로 관리되는 전체 여행 경로를 파악해야 합니다. 각 단계에서 데이터가 어떻게 변환되고 전달되는지 이해하면, 버그가 발생했을 때 어느 지점에서 문제가 생겼는지 빠르게 찾을 수 있습니다.

// 1단계: MongoDB에서 데이터 조회
const posts = await Post.find({ status: 'active' });

// 2단계: Express Controller에서 응답 형태로 변환
res.json({
  success: true,
  data: posts.map(post => ({
    id: post._id,
    title: post.name,
    content: post.description,
    createdAt: post.createdAt
  }))
});

// 3단계: React Hook에서 API 호출하여 데이터 받기
const { data } = await fetch('/api/posts').then(res => res.json());

// 4단계: Component State로 관리
const [posts, setPosts] = useState([]);
setPosts(data); // 받은 데이터를 상태로 저장

관심사 분리가 제대로 되어야 각 영역의 책임이 명확해집니다. 백엔드는 데이터 저장과 조회, 비즈니스 로직 처리에만 집중하고, 프론트엔드는 사용자 인터페이스와 상태 관리에만 집중해야 합니다. 그리고 API는 이 둘 사이의 통신 규약 역할을 합니다. 이렇게 분리되면 각 영역을 독립적으로 개발하고 테스트할 수 있어 개발 효율성이 크게 향상됩니다.

graph TB
    subgraph "프론트엔드 (React)"
        A[사용자 인터페이스]
        B[상태 관리]
        C[사용자 상호작용]
        D[React Hook]
    end
    
    subgraph "API 통신 계층"
        E[HTTP 요청/응답]
        F[JSON 데이터 교환]
    end
    
    subgraph "백엔드 (Express)"
        G[라우터]
        H[컨트롤러]
        I[비즈니스 로직]
        J[데이터 검증]
    end
    
    subgraph "데이터베이스"
        K[(MongoDB)]
        L[데이터 저장]
        M[쿼리 처리]
    end
    
    A --> D
    B --> D
    C --> D
    D -->|API 호출| E
    E --> F
    F --> G
    G --> H
    H --> I
    I --> J
    J --> K
    K --> L
    K --> M
    
    M -->|응답| J
    J -->|JSON| I
    I -->|결과| H
    H -->|응답| G
    G -->|HTTP 응답| F
    F -->|데이터| E
    E -->|상태 업데이트| D
Loading

데이터의 여행 경로를 완전히 이해하는 것이 이번 과제의 핵심입니다. 데이터가 MongoDB에서 시작해서 Express Controller를 거쳐 API Response로 변환되고, React Hook에서 받아서 Component State로 관리되는 전체 여행 경로를 파악해야 합니다. 각 단계에서 데이터가 어떻게 변환되고 전달되는지 이해하면, 버그가 발생했을 때 어느 지점에서 문제가 생겼는지 빠르게 찾을 수 있습니다.

sequenceDiagram
    participant U as 사용자
    participant C as React Component
    participant H as React Hook
    participant A as API Layer
    participant R as Express Router
    participant Ctrl as Controller
    participant M as MongoDB
    
    U->>C: 페이지 접속
    C->>H: useEffect 실행
    H->>A: fetch('/api/posts')
    A->>R: GET /api/posts
    R->>Ctrl: postController.getPosts()
    Ctrl->>M: Post.find()
    M-->>Ctrl: posts 데이터 반환
    Ctrl-->>R: JSON 응답 생성
    R-->>A: HTTP 200 + JSON
    A-->>H: 파싱된 데이터
    H-->>C: setPosts(data)
    C-->>U: 화면에 게시글 표시
    
    Note over U,M: 데이터의 완전한 여행 경로
Loading

에러 처리의 일관성을 유지하는 것이 사용자 경험에 직결됩니다. 백엔드에서 발생한 에러가 프론트엔드까지 어떻게 전달되는지, 그리고 최종적으로 사용자에게 어떻게 표시되는지 전체 플로우를 고려해야 합니다. 예를 들어 데이터베이스 연결 오류가 발생하면, 백엔드에서 적절한 에러 응답을 만들고, 프론트엔드에서는 그 에러를 받아서 사용자가 이해할 수 있는 메시지로 변환해서 보여줘야 합니다.

flowchart TD
    A[사용자 요청] --> B[React Hook]
    B --> C[API 호출]
    C --> D[Express Controller]
    D --> E[MongoDB 쿼리]
    
    E --> F{쿼리 성공?}
    F -->|성공| G[데이터 반환]
    F -->|실패| H[DB 에러 발생]
    
    G --> I[성공 응답 생성]
    H --> J[에러 응답 생성]
    
    I --> K[HTTP 200 + 데이터]
    J --> L[HTTP 500 + 에러 메시지]
    
    K --> M[React Hook에서 데이터 처리]
    L --> N[React Hook에서 에러 처리]
    
    M --> O[화면에 데이터 표시]
    N --> P[사용자에게 에러 메시지 표시]
Loading

에러 처리의 일관성을 유지하는 것이 사용자 경험에 직결됩니다. 백엔드에서 발생한 에러가 프론트엔드까지 어떻게 전달되는지, 그리고 최종적으로 사용자에게 어떻게 표시되는지 전체 플로우를 고려해야 합니다. 예를 들어 데이터베이스 연결 오류가 발생하면, 백엔드에서 적절한 에러 응답을 만들고, 프론트엔드에서는 그 에러를 받아서 사용자가 이해할 수 있는 메시지로 변환해서 보여줘야 합니다.

// 백엔드: 에러 발생 시 일관된 형태로 응답
try {
  const posts = await Post.find();
  res.json({ success: true, data: posts });
} catch (error) {
  res.status(500).json({
    success: false,
    message: '게시글을 불러오는 중 오류가 발생했습니다.',
    code: 'FETCH_POSTS_ERROR'
  });
}

// 프론트엔드: 에러 응답을 받아서 사용자에게 표시
const fetchPosts = async () => {
  try {
    const response = await fetch('/api/posts');
    const result = await response.json();
    
    if (!result.success) {
      setError(result.message); // 백엔드에서 온 에러 메시지 사용
      return;
    }
    
    setPosts(result.data);
  } catch (error) {
    setError('네트워크 오류가 발생했습니다.'); // 네트워크 에러 처리
  }
};

다음 단계 개선 방향

현재 구조에서 점진적으로 개선해 나가는 것을 권장합니다. 먼저 데이터 플로우를 명확히 이해하고, 백엔드와 프론트엔드의 역할을 분리하는 것부터 시작하세요.

특히 현재 잘 구현되어 있는 커스텀 훅들(usePagination, useDevice 등)을 기반으로 API 통신 로직을 더 체계화하면, 전체적인 데이터 플로우를 더 잘 관리할 수 있을 것입니다.

이번 과제를 통해 "백엔드가 데이터를 어떻게 관리하고, 프론트엔드가 그 데이터를 어떻게 받아서 사용자에게 보여주는지"의 전체 그림을 그려보시길 바랍니다. 어쩌다보니 피드백이 길었는데.. 도움이 되셨으면 합니다ㅎ 수고하셨어요!

@puretension puretension changed the base branch from basic to react-정미주 January 21, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

미완성 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants