diff --git a/backend/build.gradle b/backend/build.gradle index b0dc30a..ce62a40 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -62,6 +62,10 @@ dependencies { // OpenCSV implementation 'com.opencsv:opencsv:5.7.1' + // Cache + implementation 'org.springframework.boot:spring-boot-starter-cache' + implementation 'com.github.ben-manes.caffeine:caffeine' + // Servlet API //compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0' diff --git a/backend/src/main/java/_team/onmyway/config/SecurityConfig.java b/backend/src/main/java/_team/onmyway/config/SecurityConfig.java index e230686..db8f286 100644 --- a/backend/src/main/java/_team/onmyway/config/SecurityConfig.java +++ b/backend/src/main/java/_team/onmyway/config/SecurityConfig.java @@ -39,7 +39,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { "/", "/find-route", "/index.html", - //"/assets/**", + "/assets/**", "/login", "/error", "/oauth2/authorization/**", @@ -47,8 +47,9 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { "/api/auth/**", "/swagger-ui/**", "/v3/api-docs/**", - "/places/**" - //"/route/**" + "/places/**", + "/route/**", + "/api/place/**" ).permitAll() // 요청을 보낸 이가 누구이든 상관없이 통과되는 URL. .requestMatchers( "/css/**", "/js/**", "/images/**").permitAll() .anyRequest().authenticated() @@ -92,7 +93,7 @@ public CorsConfigurationSource corsConfigurationSource() { public WebSecurityCustomizer webSecurityCustomizer() { return webSecurity -> { webSecurity.ignoring() - .requestMatchers("/assets/**", "/route/**"); + .requestMatchers("/assets/**"); }; } } diff --git a/backend/src/main/java/_team/onmyway/controller/PlaceController.java b/backend/src/main/java/_team/onmyway/controller/PlaceController.java new file mode 100644 index 0000000..d63b8c1 --- /dev/null +++ b/backend/src/main/java/_team/onmyway/controller/PlaceController.java @@ -0,0 +1,23 @@ +package _team.onmyway.controller; + +import _team.onmyway.dto.PlaceDetailDTO; +import _team.onmyway.dto.UpdatePlaceDetailDTO; +import _team.onmyway.service.PlaceService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("api/place") +@RequiredArgsConstructor +public class PlaceController { + + private final PlaceService placeService; + + @GetMapping("/{id}") + public ResponseEntity getPlaceDetail(@PathVariable long id) { + PlaceDetailDTO placeDetailDTO = placeService.getHashtags(id); + return new ResponseEntity<>(placeDetailDTO, HttpStatus.OK); + } +} diff --git a/backend/src/main/java/_team/onmyway/dto/PlaceDetailDTO.java b/backend/src/main/java/_team/onmyway/dto/PlaceDetailDTO.java new file mode 100644 index 0000000..4474371 --- /dev/null +++ b/backend/src/main/java/_team/onmyway/dto/PlaceDetailDTO.java @@ -0,0 +1,8 @@ +package _team.onmyway.dto; + +import java.util.List; + +public record PlaceDetailDTO( + Long placeId, + List hashtags +) {} diff --git a/backend/src/main/java/_team/onmyway/dto/PlaceRecommendationDTO.java b/backend/src/main/java/_team/onmyway/dto/PlaceRecommendationDTO.java index 0322c36..4c03fce 100644 --- a/backend/src/main/java/_team/onmyway/dto/PlaceRecommendationDTO.java +++ b/backend/src/main/java/_team/onmyway/dto/PlaceRecommendationDTO.java @@ -9,6 +9,7 @@ @Getter @AllArgsConstructor public class PlaceRecommendationDTO { + private Long id; private String name; private double lat; private double lng; diff --git a/backend/src/main/java/_team/onmyway/dto/RecommendationResponseDTO.java b/backend/src/main/java/_team/onmyway/dto/RecommendationResponseDTO.java index 81add47..3e0391a 100644 --- a/backend/src/main/java/_team/onmyway/dto/RecommendationResponseDTO.java +++ b/backend/src/main/java/_team/onmyway/dto/RecommendationResponseDTO.java @@ -26,6 +26,7 @@ public static RecommendationResponseDTO from(List recomm @Getter @AllArgsConstructor static class PlaceInfo { + private Long placeId; private String name; private double lat; private double lng; @@ -45,6 +46,7 @@ public static PlaceInfo from(PlaceRecommendationDTO p, double userLat, double us int minutes = (int) (distance / 80); // 80m = 1분 return new PlaceInfo( + p.getId(), p.getName(), p.getLat(), p.getLng(), diff --git a/backend/src/main/java/_team/onmyway/entity/Descriptions.java b/backend/src/main/java/_team/onmyway/entity/Descriptions.java deleted file mode 100644 index e5ceb1e..0000000 --- a/backend/src/main/java/_team/onmyway/entity/Descriptions.java +++ /dev/null @@ -1,16 +0,0 @@ -package _team.onmyway.entity; - -import jakarta.persistence.*; - -@Entity -public class Descriptions { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @OneToOne - @JoinColumn(name="place_id", nullable = false) - private Place place; - - private String description; // 한 줄 소개 -} diff --git a/backend/src/main/java/_team/onmyway/entity/Place.java b/backend/src/main/java/_team/onmyway/entity/Place.java index 12b1761..7e3aabe 100644 --- a/backend/src/main/java/_team/onmyway/entity/Place.java +++ b/backend/src/main/java/_team/onmyway/entity/Place.java @@ -61,6 +61,8 @@ public class Place { @OneToMany(mappedBy = "place") // 양방향 연결 private List workingTimes = new ArrayList<>(); + private String catchPhrase; + @BatchSize(size = 100) @OneToMany(mappedBy = "place") private List photos = new ArrayList<>(); diff --git a/backend/src/main/java/_team/onmyway/repository/HashtagMappingRepository.java b/backend/src/main/java/_team/onmyway/repository/HashtagMappingRepository.java new file mode 100644 index 0000000..2201d9f --- /dev/null +++ b/backend/src/main/java/_team/onmyway/repository/HashtagMappingRepository.java @@ -0,0 +1,14 @@ +package _team.onmyway.repository; + +import _team.onmyway.entity.HashtagMapping; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface HashtagMappingRepository extends JpaRepository { + @Query("select h.hashTag.tag from HashtagMapping h join h.hashTag where h.place.id =:placeId") + public List findHashtagByPlaceId(Long placeId); +} diff --git a/backend/src/main/java/_team/onmyway/service/ImageService.java b/backend/src/main/java/_team/onmyway/service/ImageService.java index 7e9876d..c222269 100644 --- a/backend/src/main/java/_team/onmyway/service/ImageService.java +++ b/backend/src/main/java/_team/onmyway/service/ImageService.java @@ -3,6 +3,8 @@ import _team.onmyway.entity.Photos; import _team.onmyway.entity.Place; import _team.onmyway.repository.PhotosRepository; +import com.github.benmanes.caffeine.cache.Cache; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; @@ -18,6 +20,7 @@ public class ImageService { private final WebClient webClient; private final PhotosRepository photosRepository; + private final Cache placePhotoCache; @Value("${naver.api.clientId}") private String naverClientId; @@ -25,8 +28,9 @@ public class ImageService { @Value("${naver.api.clientSecret}") private String naverClientSecret; - public ImageService(PhotosRepository photosRepository) { + public ImageService(PhotosRepository photosRepository, Cache placePhotoCache) { this.photosRepository = photosRepository; + this.placePhotoCache = placePhotoCache; this.webClient = WebClient.builder() .baseUrl("https://openapi.naver.com") .build(); @@ -37,8 +41,14 @@ public Mono getImageURL(Place p) { if (hasPhoto) { return Mono.just(photosRepository.findFirstByPlaceId(p.getId()).get().getPhotoURL()); } else { - String distinct = p.getAddress().split(" ")[2]; + String cacheURL = placePhotoCache.getIfPresent(p.getId()); + if (cacheURL != null) { + return Mono.just(cacheURL); + } + + String distinct = p.getAddress(); String name = p.getName(); + return webClient.get() .uri(uri -> uri .path("/v1/search/image") diff --git a/backend/src/main/java/_team/onmyway/service/PlaceService.java b/backend/src/main/java/_team/onmyway/service/PlaceService.java new file mode 100644 index 0000000..99c36dd --- /dev/null +++ b/backend/src/main/java/_team/onmyway/service/PlaceService.java @@ -0,0 +1,25 @@ +package _team.onmyway.service; + +import _team.onmyway.dto.PlaceDetailDTO; +import _team.onmyway.dto.UpdatePlaceDetailDTO; +import _team.onmyway.entity.HashtagMapping; +import _team.onmyway.repository.HashtagMappingRepository; +import _team.onmyway.repository.PlaceRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class PlaceService { + + private final HashtagMappingRepository hashtagMappingRepository; + + public PlaceDetailDTO getHashtags(Long placeId) { + List hashtagMappings = hashtagMappingRepository.findHashtagByPlaceId(placeId); + + PlaceDetailDTO placeDetail = new PlaceDetailDTO(placeId, hashtagMappings); + return placeDetail; + } +} diff --git a/backend/src/main/java/_team/onmyway/service/RecommendationService.java b/backend/src/main/java/_team/onmyway/service/RecommendationService.java index c93f668..5d4412c 100644 --- a/backend/src/main/java/_team/onmyway/service/RecommendationService.java +++ b/backend/src/main/java/_team/onmyway/service/RecommendationService.java @@ -308,6 +308,7 @@ private PlaceRecommendationDTO toPlaceRecommendationDTO(Place place, double user double distance = geoDistanceService.distanceMeters(userLat, userLng, place.getLat(), place.getLng()); int walkingMinutes = geoDistanceService.estimateWalkingMinutes(distance); return new PlaceRecommendationDTO( + place.getId(), place.getName(), place.getLat(), place.getLng(), diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 8e6f165..aa4d73e 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -45,6 +45,6 @@ jwt.refresh-expiration=${REFRESH_TOKEN_EXPIRATION} #Tmap api tmap.api.key=${TMAP_API_KEY} -#Naver api +# Naver api naver.api.clientId=${NAVER_CLIENT_ID} naver.api.clientSecret=${NAVER_CLIENT_SECRET} \ No newline at end of file