문제
API 명세서
1. 음식점 API
1.1. 음식점 목록 조회
다양한 조건으로 음식점 목록을 검색하고 조회할 수 있는 API입니다.
- URL: /api/v1/restaurants
- Method: GET
- 설명:
- 페이징 처리된 음식점 목록을 반환합니다.
- 카테고리 필터링과 키워드 검색을 지원합니다.
- 음식점의 평균 평점과 리뷰 수를 포함합니다.
- 최신 등록순으로 정렬됩니다.
- Query Parameters:
page: 페이지 번호 (기본값: 1)
size: 페이지 크기 (기본값: 10)
category: 카테고리 필터 (선택) - 한식, 중식, 일식, 양식 등
keyword: 검색어 - 이름, 주소 검색 (선택, 최소 2글자 이상)
- Response:
{
"content": [
{
"id": 1,
"name": "맛있는 식당",
"category": "한식",
"address": "서울시 강남구",
"phone": "02-1234-5678",
"description": "전통 한식집",
"avgRating": 4.5,
"reviewCount": 100,
"createdAt": "2024-12-27T10:00:00"
}
],
"pageable": {
"page": 1,
"size": 10,
"totalElements": 100,
"totalPages": 10
}
}
1.2. 음식점 상세 조회
특정 음식점의 상세 정보와 메뉴 목록을 조회하는 API입니다.
- URL: /api/v1/restaurants/{id}
- Method: GET
- 설명:
- 음식점의 기본 정보를 제공합니다.
- 해당 음식점의 전체 메뉴 목록을 함께 반환합니다.
- 평균 평점과 총 리뷰 수를 포함합니다.
- 각 메뉴별 리뷰 수도 함께 제공됩니다.
- 존재하지 않는 음식점 ID인 경우 404 에러를 반환합니다.
- Response:
{
"restaurant": {
"id": 1,
"name": "맛있는 식당",
"category": "한식",
"address": "서울시 강남구",
"phone": "02-1234-5678",
"description": "전통 한식집",
"avgRating": 4.5,
"reviewCount": 100,
"createdAt": "2024-12-27T10:00:00",
},
"menus": [
{
"id": 1,
"name": "김치찌개",
"price": 8000,
"description": "돼지고기 김치찌개",
"category": "찌개",
"reviewCount": 50
}
]
}
2. 메뉴 API
2.1. 음식점별 메뉴 목록 조회
특정 음식점의 메뉴 목록을 조회하는 API입니다.
- URL: /api/v1/restaurants/{restaurantId}/menus
- Method: GET
- 설명:
- 특정 음식점의 전체 메뉴를 페이징 처리하여 제공합니다.
- 카테고리별 필터링을 지원합니다.
- 각 메뉴의 리뷰 수를 포함합니다.
- 메뉴는 카테고리별, 가격순으로 정렬 가능합니다.
- 존재하지 않는 음식점 ID인 경우 404 에러를 반환합니다.
- Query Parameters:
page: 페이지 번호 (기본값: 1)
size: 페이지 크기 (기본값: 10)
category: 메뉴 카테고리 필터 (선택)
sort: 정렬 기준 (price_asc, price_desc, name_asc)
- Response:
{
"content": [
{
"id": 1,
"name": "김치찌개",
"price": 8000,
"description": "돼지고기 김치찌개",
"category": "찌개",
"reviewCount": 50,
"createdAt": "2024-12-27T10:00:00"
}
],
"pageable": {
"page": 1,
"size": 10,
"totalElements": 50,
"totalPages": 5
}
}
3. 리뷰 API
3.1. 리뷰 작성
새로운 리뷰를 작성하는 API입니다.
- URL: /api/v1/reviews
- Method: POST
- 설명:
- 인증된 사용자만 리뷰를 작성할 수 있습니다.
- 동일한 메뉴에 대해 한 사용자는 하나의 리뷰만 작성 가능합니다.
- 리뷰 작성 시 해당 음식점의 평균 평점이 자동으로 갱신됩니다.
- 음식점과 메뉴의 리뷰 카운트가 자동으로 증가합니다.
- Request Header:
Authorization: Bearer {accessToken}
- Request Body:
{
"restaurantId": 1,
"menuId": 1,
"rating": 5,
"content": "정말 맛있었습니다!"
}
- Validation:
- rating: 1-5 사이의 정수만 가능
- content: 최소 10자 이상 작성
- restaurantId: 유효한 음식점 ID
- menuId: 해당 음식점의 유효한 메뉴 ID
- Response:
- 성공 201 문제있을경우 400
3.2. 리뷰 수정
기존 리뷰를 수정하는 API입니다.
- URL: /api/v1/reviews/{id}
- Method: PUT
- 설명:
- 리뷰 작성자만 수정이 가능합니다.
- 리뷰 수정 시 음식점의 평균 평점이 자동으로 갱신됩니다.
- 리뷰 내용과 평점만 수정 가능합니다.
- 존재하지 않는 리뷰 ID인 경우 404 에러를 반환합니다.
- Request Header:
Authorization: Bearer {accessToken}
- Request Body:
{
"rating": 4,
"content": "맛있었지만 조금 짰어요"
}
- Response:
- 성공 200 문제있을경우 400
4. 사용자 API
4.1. 회원가입
새로운 사용자를 등록하는 API입니다.
- URL: /api/v1/users/signup
- Method: POST
- 설명:
- 새로운 사용자를 시스템에 등록합니다.
- 이메일 중복 확인을 수행합니다.
- 비밀번호는 암호화하여 저장됩니다.
- 기본 사용자 권한(USER)으로 생성됩니다.
- Request Body:
{
"email": "user@example.com",
"password": "password123",
"nickname": "사용자닉네임"
}
- Validation:
- email: 유효한 이메일 형식 (@포함)
- password: 최소 8자 이상, 영문/숫자/특수문자 조합
- nickname: 2-20자 이내, 한글/영문/숫자 허용
- Response:
- 201
4.2. 로그인
사용자 인증을 위한 로그인 API입니다.
- URL: /api/v1/users/login
- Method: POST
- 설명:
- 이메일과 비밀번호로 사용자를 인증합니다.
- 인증 성공 시 JWT 토큰을 발급합니다.
- Access Token의 유효기간은 1시간입니다.
- Request Body:
{
"email": "user@example.com",
"password": "password123"
}
- Response:
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}
공통 사항
1. 에러 처리
주요 에러 상황:
- 400 Bad Request: 잘못된 요청 파라미터
- 401 Unauthorized: 인증 실패 또는 토큰 만료
- 403 Forbidden: 권한 없음
- 404 Not Found: 리소스를 찾을 수 없음
- 409 Conflict: 데이터 충돌 (예: 이메일 중복)
- 500 Internal Server Error: 서버 오류
2. 페이징 처리
페이징이 적용된 모든 API는 다음 정보를 포함합니다:
- page: 현재 페이지 번호 (1부터 시작)
- size: 페이지당 항목 수
- totalElements: 전체 데이터 수
- totalPages: 전체 페이지 수
3. 인증
- Bearer 토큰 방식의 JWT 인증을 사용합니다.
- 인증이 필요한 API의 경우 반드시 Authorization 헤더에 토큰을 포함해야 합니다.
4. API 버전 관리
- URI의 /api/v1 prefix로 API 버전을 관리합니다.
- 향후 하위 호환성이 깨지는 변경사항이 있을 경우 버전이 변경될 수 있습니다.
풀이
4.1. 회원가입
새로운 사용자를 등록하는 API입니다.
- URL: /api/v1/users/signup
- Method: POST
- 설명:
- 새로운 사용자를 시스템에 등록합니다.
- 이메일 중복 확인을 수행합니다.
- 비밀번호는 암호화하여 저장됩니다.
- 기본 사용자 권한(USER)으로 생성됩니다.
Postman
- Request Body:
intelliJ
entity패키지생성 - Use 클래스 생성
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
@Column(length = 100, unique = true)
public String email;
@Column(length = 256)
public String password;
@Column(length = 100)
public String nickname;
@Column(length = 100)
public String role;
@Column
public Instant createdAt;
@PrePersist
public void prePersist() {
createdAt = Instant.now();
}
}
@어노테이션 설명
클래스 레벨
@Entity
- 설명: 해당 클래스가 JPA 엔티티임을 선언합니다. JPA는 이 클래스를 데이터베이스의 테이블로 매핑합니다.
- 적용 대상: 클래스
- 예시: User 클래스는 데이터베이스의 user 테이블과 연결됩니다.
@Table
- 설명: 데이터베이스 테이블의 이름을 명시적으로 지정합니다. 지정하지 않으면 클래스 이름을 테이블 이름으로 사용합니다.
- 속성:
- name: 테이블 이름을 지정.
- 예시: @Table(name = "user")는 이 클래스가 user 테이블과 매핑된다는 것을 명시합니다.
필드 레벨
@Id
- 설명: 해당 필드를 엔티티의 기본 키(primary key)로 지정합니다.
- 적용 대상: 필드 또는 메서드.
- 예시: id 필드는 데이터베이스의 기본 키로 사용됩니다.
@GeneratedValue
- 설명: 기본 키 값 생성 전략을 지정합니다.
- 속성:
- strategy: 기본 키 생성 전략을 지정.
- GenerationType.IDENTITY: 데이터베이스에 의해 자동으로 생성됩니다 (예: MySQL의 AUTO_INCREMENT).
- strategy: 기본 키 생성 전략을 지정.
- 예시: @Id @GeneratedValue(strategy = GenerationType.IDENTITY)는 기본 키를 데이터베이스에서 자동으로 생성하도록 설정합니다.
@Column
- 설명: 데이터베이스 테이블의 열(column)을 매핑합니다.
- 속성:
- length: 문자열의 최대 길이를 지정.
- unique: 고유 값 제약 조건 설정.
- 예시:
- @Column(length = 100, unique = true)는 email 열의 길이를 100으로 제한하고 고유 값 제약 조건을 추가합니다.
- @Column(length = 256)은 password 열의 최대 길이를 256으로 지정합니다.
메서드 레벨
@PrePersist
- 설명: 엔티티가 저장되기 전에 실행되는 메서드에 적용됩니다.
- 용도: 자동으로 생성/저장되는 데이터 초기화에 사용됩니다.
- 예시: prePersist() 메서드는 엔티티가 데이터베이스에 저장되기 전에 createdAt 필드에 현재 시간을 설정합니다.
Lombok 어노테이션
@Data
- 설명: Lombok에서 제공하는 어노테이션으로, 여러 필수 메서드를 자동으로 생성합니다.
- 자동 생성되는 메서드:
- Getter/Setter: 모든 필드에 대해 getter와 setter를 생성.
- toString(): 객체 정보를 문자열로 반환하는 메서드.
- hashCode() & equals(): 객체 비교에 사용하는 메서드.
- RequiredArgsConstructor: final이나 @NonNull 필드에 대한 생성자.
- 적용 대상: 클래스.
- 예시: @Data는 User 클래스에 대해 필수 메서드들을 자동 생성합니다.
코드의 동작 과정
- 엔티티 생성: User 객체 생성.
- 데이터 저장: JPA가 @PrePersist가 붙은 메서드를 호출하여 createdAt을 설정.
- 테이블 매핑: @Entity와 @Table에 따라 데이터가 user 테이블에 저장.
controller 패키지 생성 - UserController 클래스 생성
@PostMapping("/api/v1/users/signup")
signUp(@RequestBody UserRequest userRequest) {
userService.signUp(userRequest);
}
}
signUp(UserRequest userRequest) {
// 이미 회원가입 했는지 확인
if (userRepository.existsByEmail(userRequest.email)) {
throw new RuntimeException();
}
// 비번을 암호화
userRequest.password = passwordEncoder.encode(userRequest.password);
}
repository패키지 - RestauantRepository클래스 생성
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
public Boolean existsByEmail(String email);
}
- Request Body:
{
"email": "user@example.com",
"password": "password123",
"nickname": "사용자닉네임"
}
- Validation:
- email: 유효한 이메일 형식 (@포함)
- password: 최소 8자 이상, 영문/숫자/특수문자 조합
- nickname: 2-20자 이내, 한글/영문/숫자 허용
DTO 클래스 생성 팁
AI 를 활용하여 간단하게 dto 클래스를 생성할 수 있습니다.
아래 예시의 경우 chatGPT를 이용하여 생성한 dto 클래스 입니다.
예시)
public class UserRequest {
@Email(message = "Invalid email format")
@NotBlank(message = "Email is required")
private String email;
@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*(),.?\":{}|<>])[A-Za-z\\d!@#$%^&*(),.?\":{}|<>]{8,}$",
message = "Password must contain at least one letter, one number, and one special character")
private String password;
@NotBlank(message = "Nickname is required")
@Size(min = 2, max = 20, message = "Nickname must be between 2 and 20 characters")
@Pattern(regexp = "^[a-zA-Z0-9가-힣]{2,20}$", message = "Nickname can only contain letters, numbers, and Korean characters")
private String nickname;
dto 패키지 생성 - UserRequest 클래스 생성
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserRequest {
@Email
public String email;
@Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\\\d)(?=.*[@$!%*?&])[A-Za-z\\\\d@$!%*?&]{8,}$")
public String password;
@Size(min = 2, max = 20)
@Pattern(regexp = "^[a-zA-Z0-9가-힣]+$")
public String nickname;
}
- Response:
- 201
public void signUp(UserRequest userRequest) {
// 이미 회원가입 했는지 확인
if (userRepository.existsByEmail(userRequest.email)) {
throw new RuntimeException();
}
// 비번을 암호화
userRequest.password = passwordEncoder.encode(userRequest.password);
// DB에 저장.
// DTO 를 entity로 만들어준다.
User user = new User();
user.email = userRequest.email;
user.password = userRequest.password;
user.nickname = userRequest.nickname;
user.role = "USER";
userRepository.save(user);
}
@PostMapping("/api/v1/users/signup")
public ResponseEntity<Object> signUp(@RequestBody UserRequest userRequest) {
try {
userService.signUp(userRequest);
return ResponseEntity.status(201).build();
} catch (Exception e) {
return ResponseEntity.status(400).build();
}
}
Postman
4.2. 로그인
사용자 인증을 위한 로그인 API입니다.
- URL: /api/v1/users/login
- Method: POST
- 설명:
- 이메일과 비밀번호로 사용자를 인증합니다.
- 인증 성공 시 JWT 토큰을 발급합니다.
- Access Token의 유효기간은 1시간입니다.
Postman
{
"email": "user@example.com",
"password": "password123"
}
intelliJ
@PostMapping("/api/v1/users/signup")
signUp(@RequestBody UserRequest userRequest) {
userService.signUp(userRequest);
login(UserRequest userRequest) {
// 1. 이메일로 유저가 있는지 확인한다.
if (!userRepository.existsByEmail(userRequest.email)) {
throw new RuntimeException();
}
// 2. 테이블에 이 이메일로 유저가 있어야 한다. 유저데이터를 가져온다
User user = userRepository.findByEmail(userRequest.email);
// 3. DB 에서 가져온 비밀번호와 입력된 비밀번호가 같은지 확인한다.
if (!passwordEncoder.matches(userRequest.password, user.password)) {
throw new RuntimeException();
}
// 3. 토큰을 생성한다.
String token = jwtConfig.createToken(user.id);
- Response:
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}
'API > 실습' 카테고리의 다른 글
81. [JAVA] API 문서 문제 : JDBC -> JPA 진행 (3) (1) | 2025.01.16 |
---|---|
78. [JAVA] API 문서 문제 : JDBC -> JPA 진행 (2) (0) | 2025.01.15 |
69. [ JAVA ] 공공데이터 포털 API 이용 : 영화 박스오피스순위 예시 (0) | 2025.01.13 |
63. [ JAVA ] Spring Boot를 이용한 API 통신 : 유튜브 검색 예시 (1) | 2025.01.12 |
61. [ JAVA ] API 리뷰 작성시 사진 추가 업로드 구현 (0) | 2025.01.12 |