🎯 AI 브레인스토밍 플랫폼 개발 일지 - Day 4

📅 작업 일자

2025년 11월 27일 (Day 4)

🎯 오늘의 목표

  • Controller 계층 구현 (REST API)
  • Postman으로 API 테스트
  • CRUD 완전 동작 확인

✅ 완료 사항

1️⃣ REST API 설계

API 엔드포인트를 먼저 설계했다.

User API (4개)

Method Endpoint 설명
POST /api/users 회원가입
GET /api/users/{id} 회원 조회
GET /api/users/email?email= 이메일로 조회
DELETE /api/users/{id} 회원 탈퇴

Idea API (5개)

Method Endpoint 설명
POST /api/ideas 아이디어 저장
GET /api/ideas/{id} 아이디어 조회
GET /api/ideas?userId= 사용자별 조회
GET /api/ideas/count?userId= 개수 조회
DELETE /api/ideas/{id} 아이디어 삭제

Inquiry API (4개)

Method Endpoint 설명
POST /api/inquiries 문의 작성
GET /api/inquiries/{id} 문의 조회
GET /api/inquiries?userId= 사용자별 조회
DELETE /api/inquiries/{id} 문의 삭제

총 13개 API 설계 완료


2️⃣ Controller 구현

UserController

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    // 회원가입
    @PostMapping
    public ResponseEntity<UserResponseDto> createUser(@RequestBody UserRequestDto requestDto) {
        User user = requestDto.toEntity();
        User savedUser = userService.save(user);
        return ResponseEntity.ok(UserResponseDto.from(savedUser));
    }

    // 회원 조회
    @GetMapping("/{id}")
    public ResponseEntity<UserResponseDto> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return ResponseEntity.ok(UserResponseDto.from(user));
    }

    // 이메일로 조회
    @GetMapping("/email")
    public ResponseEntity<UserResponseDto> getUserByEmail(@RequestParam String email) {
        Optional<User> userOptional = userService.findByEmail(email);
        User user = userOptional.orElseThrow(
            () -> new RuntimeException("해당 이메일의 사용자가 없습니다.")
        );
        return ResponseEntity.ok(UserResponseDto.from(user));
    }

    // 회원 탈퇴
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

특징:

  • @RestController - REST API용 컨트롤러
  • @RequiredArgsConstructor - Service 자동 주입
  • @RequestBody - JSON을 DTO로 자동 변환
  • @PathVariable - URL 경로 변수 (/users/1에서 1)
  • @RequestParam - 쿼리 파라미터 (?email=test@gmail.com)
  • ResponseEntity - HTTP 상태 코드 + 응답 바디

IdeaController

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/ideas")
public class IdeaController {

    private final IdeaService ideaService;

    // 아이디어 생성
    @PostMapping
    public ResponseEntity<IdeaResponseDto> createIdea(@RequestBody IdeaRequestDto requestDto) {
        Idea idea = requestDto.toEntity();
        Idea saveIdea = ideaService.save(idea);
        return ResponseEntity.ok(IdeaResponseDto.from(saveIdea));
    }

    // 아이디어 조회
    @GetMapping("/{id}")
    public ResponseEntity<IdeaResponseDto> getIdea(@PathVariable Long id) {
        Idea idea = ideaService.findById(id);
        return ResponseEntity.ok(IdeaResponseDto.from(idea));
    }

    // 사용자별 전체 조회
    @GetMapping
    public ResponseEntity<List<IdeaResponseDto>> getIdeasByUser(@RequestParam Long userId) {
        List<Idea> ideas = ideaService.findByUserId(userId);
        List<IdeaResponseDto> responseDtos = ideas.stream()
                .map(IdeaResponseDto::from)
                .toList();
        return ResponseEntity.ok(responseDtos);
    }

    // 아이디어 삭제
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteIdea(@PathVariable Long id) {
        ideaService.delete(id);
        return ResponseEntity.noContent().build();
    }

    // 아이디어 개수 조회
    @GetMapping("/count")
    public ResponseEntity<Long> countIdeas(@RequestParam Long userId) {
        long count = ideaService.countByUserId(userId);
        return ResponseEntity.ok(count);
    }
}

특징:

  • List<IdeaResponseDto> - 여러 개 반환
  • stream().map().toList() - List → List 변환
  • ResponseEntity<Long> - 숫자 반환

InquiryController

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/inquiries")
public class InquiryController {

    private final InquiryService inquiryService;

    // 문의 작성
    @PostMapping
    public ResponseEntity<InquiryResponseDto> createInquiry(@RequestBody InquiryRequestDto requestDto) {
        Inquiry inquiry = requestDto.toEntity();
        Inquiry savedInquiry = inquiryService.save(inquiry);
        return ResponseEntity.ok(InquiryResponseDto.from(savedInquiry));
    }

    // 문의 조회
    @GetMapping("/{id}")
    public ResponseEntity<InquiryResponseDto> getInquiry(@PathVariable Long id) {
        Inquiry inquiry = inquiryService.findById(id);
        return ResponseEntity.ok(InquiryResponseDto.from(inquiry));
    }

    // 사용자별 문의 조회
    @GetMapping
    public ResponseEntity<List<InquiryResponseDto>> getInquiries(@RequestParam Long userId) {
        List<Inquiry> inquiries = inquiryService.findByUserId(userId);
        List<InquiryResponseDto> responseDtos = inquiries.stream()
                .map(InquiryResponseDto::from)
                .toList();
        return ResponseEntity.ok(responseDtos);
    }

    // 문의 삭제
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteInquiry(@PathVariable Long id) {
        inquiryService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

3️⃣ Postman 테스트

User API 테스트

1. POST - 회원가입

POST http://localhost:8080/api/users
Content-Type: application/json

{
  "email": "test@gmail.com",
  "username": "홍길동",
  "provider": "GOOGLE",
  "providerId": "google-123456"
}

응답 (200 OK):
{
  "userId": 1,
  "email": "test@gmail.com",
  "username": "홍길동",
  "provider": "GOOGLE",
  "createdAt": "2025-11-27T15:30:00"
}

2. GET - 회원 조회

GET http://localhost:8080/api/users/1

응답 (200 OK):
{
  "userId": 1,
  "email": "test@gmail.com",
  "username": "홍길동",
  "provider": "GOOGLE",
  "createdAt": "2025-11-27T15:30:00"
}

3. GET - 이메일로 조회

GET http://localhost:8080/api/users/email?email=test@gmail.com

응답 (200 OK):
동일한 결과

4. DELETE - 회원 탈퇴

DELETE http://localhost:8080/api/users/1

응답 (204 No Content):
(응답 바디 없음)

Idea API 테스트

1. POST - 아이디어 생성

POST http://localhost:8080/api/ideas
Content-Type: application/json

{
  "userId": 1,
  "title": "학습 앱 아이디어",
  "content": "AI 기반 맞춤형 학습 플랫폼",
  "purpose": "학생들의 학습 효율 향상"
}

응답 (200 OK):
{
  "ideaId": 1,
  "userId": 1,
  "title": "학습 앱 아이디어",
  "content": "AI 기반 맞춤형 학습 플랫폼",
  "purpose": "학생들의 학습 효율 향상"
}

2. GET - 사용자별 조회

GET http://localhost:8080/api/ideas?userId=1

응답 (200 OK):
[
  {
    "ideaId": 1,
    "userId": 1,
    "title": "학습 앱 아이디어",
    ...
  }
]

3. GET - 개수 조회

GET http://localhost:8080/api/ideas/count?userId=1

응답 (200 OK):
1

Inquiry API 테스트

1. POST - 문의 작성

POST http://localhost:8080/api/inquiries
Content-Type: application/json

{
  "userId": 1,
  "title": "로그인 오류",
  "content": "구글 로그인이 안 돼요"
}

응답 (200 OK):
{
  "inquiryId": 1,
  "userId": 1,
  "title": "로그인 오류",
  "content": "구글 로그인이 안 돼요",
  "status": "PENDING",
  "reply": null
}

전체 API 테스트 성공!


💡 배운 점 & 트러블슈팅

1. @PathVariable vs @RequestParam

@PathVariable: URL 경로의 일부

@GetMapping("/users/{id}")
public void getUser(@PathVariable Long id) {
// GET /api/users/1 → id = 1

@RequestParam: 쿼리 파라미터 (? 뒤)

@GetMapping("/ideas")
public void getIdeas(@RequestParam Long userId) {
// GET /api/ideas?userId=1 → userId = 1

언제 쓸까?

  • PathVariable: "몇 번?" (특정 리소스)
  • RequestParam: "누구의?" (필터링/검색)

2. DTO 변환의 중요성

왜 Entity를 직접 반환하면 안 되나?

  1. 보안 문제: providerId, role 같은 민감 정보 노출
  2. Mass Assignment 공격: 클라이언트가 role을 ADMIN으로 변경 시도
  3. API 스펙 독립성: Entity 변경 시 API 스펙도 변경됨

올바른 흐름:

클라이언트
    ↓ (JSON)
RequestDto
    ↓ toEntity()
Entity
    ↓ Service 처리
Entity
    ↓ from(entity)
ResponseDto
    ↓ (JSON)
클라이언트

3. List 처리 패턴

Entity List → DTO List 변환:

List<Idea> ideas = ideaService.findByUserId(userId);
List<IdeaResponseDto> responseDtos = ideas.stream()
    .map(IdeaResponseDto::from)
    .toList();

Stream API:

  • stream(): List를 Stream으로 변환
  • map(): 각 요소를 변환 (Idea → IdeaResponseDto)
  • toList(): 다시 List로 변환

4. HTTP 상태 코드

상태 코드 의미 사용
200 OK 성공 조회, 생성 성공
204 No Content 성공 (응답 없음) 삭제 성공
404 Not Found 없음 리소스 없음
500 Internal Server Error 서버 오류 예외 발생

📊 프로젝트 현황

완료된 계층

✅ Entity (User, Idea, Inquiry)
✅ Repository (Spring Data JPA)
✅ Service (비즈니스 로직)
✅ DTO (Request/Response)
✅ Controller (REST API)
✅ Postman 테스트

디렉토리 구조

src/main/java/com/brainstorming/brainstorming_platform/
├── domain/
│   ├── user/
│   │   ├── entity/User.java
│   │   ├── repository/UserRepository.java
│   │   ├── service/UserService.java
│   │   ├── controller/UserController.java ✅
│   │   └── dto/
│   │       ├── UserRequestDto.java
│   │       └── UserResponseDto.java
│   ├── idea/
│   │   ├── entity/Idea.java
│   │   ├── repository/IdeaRepository.java
│   │   ├── service/IdeaService.java
│   │   ├── controller/IdeaController.java ✅
│   │   └── dto/
│   │       ├── IdeaRequestDto.java
│   │       └── IdeaResponseDto.java
│   └── inquiry/
│       ├── entity/Inquiry.java
│       ├── repository/InquiryRepository.java
│       ├── service/InquiryService.java
│       ├── controller/InquiryController.java ✅
│       └── dto/
│           ├── InquiryRequestDto.java
│           └── InquiryResponseDto.java
└── global/
    └── BaseEntity.java

🎯 다음 단계 (Day 5)

우선순위 1: Python 연동 🔥 (핵심!)

1. Python FastAPI 준비
   - 기존 브레인스토밍 모듈 정리
   - FastAPI 엔드포인트 생성

2. Spring-Python 연동
   - RestTemplate or WebClient
   - BrainstormingService 작성
   - BrainstormingController 작성

3. 통합 테스트
   - 질문 → Python → 아이디어 생성 → DB 저장

우선순위 2: OAuth2 로그인

1. Google/Kakao 로그인
2. JWT 토큰 발급 (선택)
3. Security 설정 강화

우선순위 3: 프론트엔드

1. React or Vue.js
2. 로그인/회원가입 UI
3. 브레인스토밍 채팅 UI
4. 아이디어 관리 UI

📚 참고 자료

  • Spring Boot REST API 공식 문서
  • Postman 공식 문서
  • RESTful API 설계 가이드

🔗 GitHub

  • Repository: brainstorming-platform
  • Commit: Day 4 - Controller 구현 및 Postman 테스트 완료

💭 회고

잘한 점

  • Controller 3개를 패턴에 따라 일관되게 구현
  • Postman으로 13개 API 전체 테스트 완료
  • @PathVariable과 @RequestParam의 차이 이해
  • DTO 변환의 필요성 완전 이해

개선할 점

  • 예외 처리가 RuntimeException으로만 되어 있음
  • Validation 추가 필요 (@Valid, @NotNull)
  • 테스트 코드 추가 (MockMvc)

느낀 점

"클론코딩으로 막 치는 것보다 뭔가 좀 더 내 머릿속에 들어오는 것 같으면서 손으로 모래잡듯이 바로 흩어지기도 하네"

이 느낌은 정상적인 학습 과정입니다!

  • 개념을 이해하고 (✅)
  • 직접 코드를 작성하고 (✅)
  • 실제로 동작하는 걸 확인했으니 (✅)
  • 이제 반복하면서 점점 손에 익을 것입니다!

Day 4 완료! 수고하셨습니다! 🎉

오늘의 성과:

  • REST API 13개 완성 ✅
  • CRUD 완전 동작 확인 ✅
  • Controller 패턴 이해 ✅
  • Postman 테스트 마스터 ✅

내일은 핵심 기능인 Python 연동을 시작합니다! 🚀

+ Recent posts