diff --git a/README.md b/README.md
index a3cad8d..b73a1f4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,59 @@
-# TagCafe-BE
\ No newline at end of file
+# TagCafe Backend
+
+**카공에 진심인 당신을 위한 카페 플랫폼, TagCafe**
+
+와이파이, 책상 크기, 콘센트 등 카공에 중요한 요소들을 태그 기반으로 필터링하고,
+리뷰와 제보를 통해 사용자에게 딱 맞는 카페를 찾아주는 서비스입니다.
+
+---
+
+## 🌐 배포 주소
+
+- 웹 서비스: [https://tagcafe.site](https://tagcafe.site)
+- Swagger API 문서: [https://tagcafe.site/swagger-ui/index.html](https://tagcafe.site/swagger-ui/index.html)
+
+
+
+## 🧑💻 역할
+
+| 이름 | 역할 |
+|:----:|----------------------------------------------------------------|
+|
| 카페 검색 및 조회, 태그 필터링, 카페 저장 기능 개발
Swagger를 활용한 전체 API 문서화 |
+| | 회원 관리, 마이페이지(리뷰/제보) 기능 구현
카카오 로그인 연동
CI/CD 설정 및 배포 자동화 |
+
+
+
+## 🛠 기술 스택
+
+| 항목 | 내용 |
+|-------------|------------------------------------------------------------------|
+| 언어 | Java 17 |
+| 프레임워크 | Spring Boot 3.4.2 |
+| ORM | Spring Data JPA |
+| 빌드 도구 | Gradle |
+| DB | MySQL 8.0 (AWS RDS) |
+| 인증 | Spring Security + Kakao OAuth 2.0 + JWT |
+| 배포 | Docker + Docker Compose + NGINX |
+| CI/CD | GitHub Actions → EC2 자동 배포 |
+| 웹서버 | NGINX (React + API 리버스 프록시) |
+| 인증서 | Let’s Encrypt (HTTPS 인증서 적용) |
+| 환경 변수 | dotenv-java 기반 `.env`, `application.yml` 에서 외부 주입 |
+| API 문서화 | Swagger (springdoc-openapi) |
+
+
+
+## 📁 프로젝트 구조
+
+```bash
+📦 TagCafe
+┣ 📂 config # 전역 설정 (보안, Swagger, 카카오 OAuth 등)
+┣ 📂 controller # API 컨트롤러
+┣ 📂 dto # 요청/응답 DTO
+┣ 📂 entity # JPA 엔티티
+┣ 📂 repository # DB 접근 (JPA 레포지토리)
+┣ 📂 service # 비즈니스 로직 처리
+┣ 📂 util # 공통 유틸 기능 (JWT)
+┗ TagCafeApplication.java # 메인 클래스
+```
+
+
diff --git a/build.gradle b/build.gradle
index 4439bec..06fedd9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -40,6 +40,7 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+ implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
}
tasks.named('test') {
diff --git a/src/main/java/com/Minjin/TagCafe/config/KakaoAuthController.java b/src/main/java/com/Minjin/TagCafe/config/KakaoAuthController.java
index e4fca10..bbf1daa 100644
--- a/src/main/java/com/Minjin/TagCafe/config/KakaoAuthController.java
+++ b/src/main/java/com/Minjin/TagCafe/config/KakaoAuthController.java
@@ -3,6 +3,8 @@
import com.Minjin.TagCafe.entity.User;
import com.Minjin.TagCafe.repository.UserRepository;
import com.Minjin.TagCafe.util.JwtUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,6 +23,7 @@
import java.util.Map;
import java.util.Optional;
+@Tag(name = "Auth", description = "카카오 로그인 및 사용자 인증 관련 API")
@RestController
@RequestMapping("/oauth/kakao")
@RequiredArgsConstructor
@@ -33,6 +36,7 @@ public class KakaoAuthController {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
+ @Operation(summary = "카카오 로그인 시작", description = "카카오 로그인 인증 페이지로 리디렉션합니다.")
@GetMapping("/login")
public RedirectView kakaoLogin() {
String kakaoAuthUrl = "https://kauth.kakao.com/oauth/authorize"
@@ -44,6 +48,7 @@ public RedirectView kakaoLogin() {
return new RedirectView(kakaoAuthUrl);
}
+ @Operation(summary = "카카오 로그인 콜백", description = "카카오 인증 후 사용자 정보를 받아옵니다.")
@GetMapping("/callback")
public RedirectView kakaoCallback(@RequestParam(name="code") String code) {
@@ -119,6 +124,7 @@ public RedirectView kakaoCallback(@RequestParam(name="code") String code) {
+ "&token=" + jwtToken);
}
+ @Operation(summary = "로그인한 사용자 정보 조회", description = "현재 로그인한 사용자의 정보를 반환합니다.")
@GetMapping("/userinfo")
public ResponseEntity getUserInfo(@RequestParam("access_token") String accessToken) {
String userInfoUrl = "https://kapi.kakao.com/v2/user/me";
diff --git a/src/main/java/com/Minjin/TagCafe/config/SwaggerConfig.java b/src/main/java/com/Minjin/TagCafe/config/SwaggerConfig.java
new file mode 100644
index 0000000..29c39ac
--- /dev/null
+++ b/src/main/java/com/Minjin/TagCafe/config/SwaggerConfig.java
@@ -0,0 +1,18 @@
+package com.Minjin.TagCafe.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SwaggerConfig {
+ @Bean
+ public OpenAPI openAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("TagCafe API")
+ .description("카공러 맞춤형 카페 정보 플랫폼 TagCafe의 API 명세서입니다.")
+ .version("v1.0.0"));
+ }
+}
diff --git a/src/main/java/com/Minjin/TagCafe/controller/CafeController.java b/src/main/java/com/Minjin/TagCafe/controller/CafeController.java
index 92b8054..eed64b4 100644
--- a/src/main/java/com/Minjin/TagCafe/controller/CafeController.java
+++ b/src/main/java/com/Minjin/TagCafe/controller/CafeController.java
@@ -6,6 +6,8 @@
import com.Minjin.TagCafe.entity.Cafe;
import com.Minjin.TagCafe.repository.CafeRepository;
import com.Minjin.TagCafe.service.CafeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -15,6 +17,7 @@
import java.util.*;
import java.util.stream.Collectors;
+@Tag(name = "Cafe", description = "카페 정보 조회 및 필터 검색 API")
@RestController
@RequestMapping("/cafes")
@RequiredArgsConstructor
@@ -24,6 +27,7 @@ public class CafeController {
private final CafeRepository cafeRepository;
// id로 카페 조회
+ @Operation(summary = "ID로 카페 조회", description = "카페 ID를 기반으로 카페 상세 정보를 조회합니다.")
@GetMapping("/{cafeId}")
public ResponseEntity getCafeById(@PathVariable("cafeId") Long cafeId) {
Cafe cafe = cafeService.getCafeById(cafeId);
@@ -54,6 +58,7 @@ public ResponseEntity getCafeById(@PathVariable("cafeId") Long cafeId)
}
// 검색
+ @Operation(summary = "카페 검색", description = "키워드로 카페를 검색합니다.")
@GetMapping("/search")
public ResponseEntity> searchCafe(@RequestParam(name = "query") String query) {
List cafes = cafeService.searchCafeByKeyword(query);
@@ -72,6 +77,7 @@ public ResponseEntity> searchCafe(@RequestParam(name = "quer
}
// 지도 영역 내 카페 조회 (위경도 범위 내 검색)
+ @Operation(summary = "지도의 특정 영역 내 카페 조회", description = "지도 상에서 특정 위경도 범위에 있는 카페 목록을 반환합니다.")
@GetMapping("/area")
public ResponseEntity> getCafesInArea(@RequestParam(name = "minLat") double minLat,
@RequestParam(name = "maxLat") double maxLat,
@@ -105,6 +111,7 @@ public ResponseEntity> getCafesInArea(@RequestParam(name = "mi
}
// 특정 태그와 특정 값을 가진 카페 조회
+ @Operation(summary = "여러 태그 조건으로 카페 필터링", description = "태그 이름과 값들을 기준으로 카페를 필터링하여 조회합니다.")
@GetMapping("/filter")
public ResponseEntity> getCafesByMultipleTags(@RequestParam(name = "tagNames") List tagNames,
@RequestParam(name = "values") List values) {
@@ -152,6 +159,7 @@ public ResponseEntity> getCafesByMultipleTags(@RequestParam(na
// admin - 카페 검색 후 db 저장
+ @Operation(summary = "admin - 카페 저장", description = "관리자가 카페 정보를 저장합니다.")
@PostMapping
public ResponseEntity> addCafe(@RequestBody CafeDto cafeDto) {
Cafe savedCafe = cafeService.addCafe(cafeDto);
@@ -159,6 +167,7 @@ public ResponseEntity> addCafe(@RequestBody CafeDto cafeDto) {
}
// admin - 태그 값 업데이트
+ @Operation(summary = "admin - 카페 태그 수정", description = "특정 카페의 태그 정보(wifi, 책상 등)를 수정합니다.")
@PutMapping("/{cafeId}/tags")
public ResponseEntity updateCafeTags(@PathVariable("cafeId") Long cafeId,
@RequestBody CafeDto cafeDto) {
@@ -176,12 +185,14 @@ public ResponseEntity updateCafeTags(@PathVariable("cafeId") Long cafeId,
}
// 모든 카페 조회 API 추가
+ @Operation(summary = "모든 카페 조회", description = "전체 카페 목록을 조회합니다.")
@GetMapping
public ResponseEntity> getAllCafes() {
List cafes = cafeService.getAllCafes();
return ResponseEntity.ok(cafes);
}
+ @Operation(summary = "카페 태그 조회", description = "카페 ID를 기준으로 태그(wifi, 콘센트 등) 정보를 조회합니다.")
@GetMapping("/{cafeId}/tags")
public ResponseEntity