diff --git a/Api/build.gradle b/Api/build.gradle index d8e6bc6..ecf35b8 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -12,7 +12,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' - + testImplementation 'org.springframework.security:spring-security-test' // swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' diff --git a/Api/src/main/java/com/example/api/recommendedPopcorn/service/PostRecommendPopcornUseCase.java b/Api/src/main/java/com/example/api/recommendedPopcorn/service/PostRecommendPopcornUseCase.java index af38aa2..10e356f 100644 --- a/Api/src/main/java/com/example/api/recommendedPopcorn/service/PostRecommendPopcornUseCase.java +++ b/Api/src/main/java/com/example/api/recommendedPopcorn/service/PostRecommendPopcornUseCase.java @@ -9,6 +9,9 @@ import com.example.domains.recommendedPopcornUser.entity.RecommendedPopcornUser; import com.example.domains.user.adaptor.UserAdaptor; import com.example.domains.user.entity.User; +import com.example.oauth.tmdb.client.TmdbClient; +import com.example.oauth.tmdb.dto.TdmbResponseDto; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonElement; @@ -26,6 +29,7 @@ public class PostRecommendPopcornUseCase { private final RecommendedPopcornAdaptor recommendedPopcornAdaptor; private final UserAdaptor userAdaptor; + private final TmdbClient tmdbClient; private final RecommendedPopcornUserAdaptor recommendedPopcornUserAdaptor; @Value("${KDMB}") @@ -38,14 +42,48 @@ public void execute(RecommendedPopcornRequest request) throws IOException { JsonElement jsonObject = parser.parse(request.getMovieId()); String movieTypeWithoutQuotes = request.getMovieType().replaceAll("\"", ""); - getApi(jsonObject,request,movieTypeWithoutQuotes); + //TODO + String responseString = tmdbClient.getMovieData(request.getMovieId(),movieTypeWithoutQuotes,"Y",tmdb); + changeToObject(responseString,request); //RecommendedPopcorn.of(request.getMovieId(),request.getReason()) } + private void changeToObject(String responseString,RecommendedPopcornRequest request) throws JsonProcessingException { + //값 처리하는 로직 + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(responseString); + //4. To JsonObject + + JsonNode movieData = rootNode.path("Data").path(0).path("Result").path(0); + + + String title = movieData.path("title").asText(); + title = title.trim(); + String directorNm = movieData.path("directors").path("director").path(0).path("directorNm").asText(); + String plotText = movieData.path("plots").path("plot").path(0).path("plotText").asText(); + String firstPosterUrl = movieData.path("posters").asText().split("\\|")[0]; + postRecommendation(request.getMovieId(),title,plotText,firstPosterUrl,directorNm,request); + } + private void validateMovieId(String movieId) { recommendedPopcornAdaptor.checkExists(movieId); } + public void postRecommendation(String movieId,String originalTitle, String overview, String posterPath,String directorNm, RecommendedPopcornRequest popcornRequest) { + Long userId = SecurityUtil.getCurrentUserId(); + User user = userAdaptor.findById(userId); + + final RecommendedPopcorn recommendedPopcorn = RecommendedPopcorn.of(movieId + ,originalTitle,posterPath,overview,directorNm,popcornRequest.getReason()); + RecommendedPopcorn recommendedPopcornResult = recommendedPopcornAdaptor.save(recommendedPopcorn); + proceedSaving(recommendedPopcornResult,user); + } + private void proceedSaving(RecommendedPopcorn recommendedPopcorn, User user) { + final RecommendedPopcornUser recommendedPopcornUser = RecommendedPopcornUser.of(true,user,recommendedPopcorn); + recommendedPopcornUserAdaptor.save(recommendedPopcornUser); + } + + private void getApi( JsonElement movieId, RecommendedPopcornRequest popcornRequest, String movieType) throws IOException { @@ -83,17 +121,4 @@ private void getApi( } } - public void postRecommendation(String movieId,String originalTitle, String overview, String posterPath,String directorNm, RecommendedPopcornRequest popcornRequest) { - Long userId = SecurityUtil.getCurrentUserId(); - User user = userAdaptor.findById(userId); - - final RecommendedPopcorn recommendedPopcorn = RecommendedPopcorn.of(movieId - ,originalTitle,posterPath,overview,directorNm,popcornRequest.getReason()); - RecommendedPopcorn recommendedPopcornResult = recommendedPopcornAdaptor.save(recommendedPopcorn); - proceedSaving(recommendedPopcornResult,user); - } - private void proceedSaving(RecommendedPopcorn recommendedPopcorn, User user) { - final RecommendedPopcornUser recommendedPopcornUser = RecommendedPopcornUser.of(true,user,recommendedPopcorn); - recommendedPopcornUserAdaptor.save(recommendedPopcornUser); - } } diff --git a/Api/src/main/java/com/example/api/user/service/CheckDuplicateUseCase.java b/Api/src/main/java/com/example/api/user/service/CheckDuplicateUseCase.java index 37b3df5..1583c79 100644 --- a/Api/src/main/java/com/example/api/user/service/CheckDuplicateUseCase.java +++ b/Api/src/main/java/com/example/api/user/service/CheckDuplicateUseCase.java @@ -17,4 +17,5 @@ public DuplicateCheckResponse execute(UpdateUserInfoRequest request) { return DuplicateCheckResponse.from(false); } } + } diff --git a/Core/build.gradle b/Core/build.gradle index f67e563..c6f96c9 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -3,10 +3,6 @@ jar.enabled = true dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' diff --git a/Infra/src/main/java/com/example/feign/config/OpenFeignConfig.java b/Infra/src/main/java/com/example/feign/config/OpenFeignConfig.java index 32817e7..8cd9805 100644 --- a/Infra/src/main/java/com/example/feign/config/OpenFeignConfig.java +++ b/Infra/src/main/java/com/example/feign/config/OpenFeignConfig.java @@ -2,6 +2,7 @@ import com.example.oauth.BaseFeignClientClass; +import com.example.oauth.tmdb.client.TmdbClient; import feign.Logger; import feign.Retryer; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; diff --git a/Infra/src/main/java/com/example/oauth/tmdb/client/TmdbClient.java b/Infra/src/main/java/com/example/oauth/tmdb/client/TmdbClient.java new file mode 100644 index 0000000..9056c12 --- /dev/null +++ b/Infra/src/main/java/com/example/oauth/tmdb/client/TmdbClient.java @@ -0,0 +1,22 @@ +package com.example.oauth.tmdb.client; + +import com.example.oauth.apple.config.AppleOAuthConfig; +import com.example.oauth.tmdb.config.TmdbClientConfig; +import com.example.oauth.tmdb.dto.TdmbResponseDto; +import feign.Headers; +import feign.Param; +import feign.Response; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; + + +@FeignClient( + name = "TmdbClient", + url = "http://api.koreafilm.or.kr/openapi-data2/wisenut/search_api/search_json2.jsp?collection=kmdb_new2", + configuration = TmdbClientConfig.class) +public interface TmdbClient { + @GetMapping + String getMovieData(@RequestParam("movieId") String movieId, @RequestParam("movieSeq") String movieSeq, @RequestParam("detail") String detail, @RequestParam("ServiceKey") String ServiceKey); +} diff --git a/Infra/src/main/java/com/example/oauth/tmdb/config/TmdbClientConfig.java b/Infra/src/main/java/com/example/oauth/tmdb/config/TmdbClientConfig.java new file mode 100644 index 0000000..f80d210 --- /dev/null +++ b/Infra/src/main/java/com/example/oauth/tmdb/config/TmdbClientConfig.java @@ -0,0 +1,30 @@ +package com.example.oauth.tmdb.config; + + +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.HttpMessageConverterCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import({TmdbErrorDecoder.class}) +public class TmdbClientConfig { + + @Bean + @ConditionalOnMissingBean(value = ErrorDecoder.class) + public TmdbErrorDecoder commonFeignErrorDecoder() { + return new TmdbErrorDecoder(); + } + + @Bean + Encoder formEncoder() { + return new feign.form.FormEncoder(); + } + + +} diff --git a/Infra/src/main/java/com/example/oauth/tmdb/config/TmdbErrorDecoder.java b/Infra/src/main/java/com/example/oauth/tmdb/config/TmdbErrorDecoder.java new file mode 100644 index 0000000..e613645 --- /dev/null +++ b/Infra/src/main/java/com/example/oauth/tmdb/config/TmdbErrorDecoder.java @@ -0,0 +1,30 @@ +package com.example.oauth.tmdb.config; + +import com.amazonaws.util.IOUtils; +import com.example.error.BaseRunTimeException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.Response; +import feign.codec.ErrorDecoder; +import lombok.SneakyThrows; + +import java.io.InputStream; + +public class TmdbErrorDecoder implements ErrorDecoder { + @Override + @SneakyThrows + public Exception decode(String methodKey, Response response) { + InputStream inputStream = response.body().asInputStream(); + byte[] byteArray = IOUtils.toByteArray(inputStream); + String responseBody = new String(byteArray); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + + String error = jsonNode.get("error") == null ? null : jsonNode.get("error").asText(); + String errorDescription = + jsonNode.get("error_description") == null + ? null + : jsonNode.get("error_description").asText(); + throw new BaseRunTimeException(response.status(), error, errorDescription); + } +} diff --git a/Infra/src/main/java/com/example/oauth/tmdb/dto/TdmbResponseDto.java b/Infra/src/main/java/com/example/oauth/tmdb/dto/TdmbResponseDto.java new file mode 100644 index 0000000..24d923f --- /dev/null +++ b/Infra/src/main/java/com/example/oauth/tmdb/dto/TdmbResponseDto.java @@ -0,0 +1,36 @@ +package com.example.oauth.tmdb.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + + +@NoArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class TdmbResponseDto { + private String movieId; + private String movieSeq; + private String title; + private String directorNm; + private String plotText; + //private String posterUrls; + public String getMovieId() { + return movieId; + } + public String getMovieSeq() { + return movieSeq; + } + public String getTitle() { + return title; + } + public String getDirectorNm() { + return directorNm; + } + public String getPlotText() { + return plotText; + } + +} diff --git a/Infra/src/main/resources/application-infra.yml b/Infra/src/main/resources/application-infra.yml index 919bd5d..968acfb 100644 --- a/Infra/src/main/resources/application-infra.yml +++ b/Infra/src/main/resources/application-infra.yml @@ -2,7 +2,7 @@ spring: config: activate: - on-profile: local + on-profile: local,test data: redis: @@ -24,6 +24,15 @@ cloud: app: firebase-configuration-file: popcornmate-d7ca1-firebase-adminsdk-svbpw-343677f710.json +test: + port: + localhost + +feign: + httpclient: + enabled: true + + --- spring: config: diff --git a/Infra/src/test/java/com/example/config/InfraIntegrateProfileResolver.java b/Infra/src/test/java/com/example/config/InfraIntegrateProfileResolver.java new file mode 100644 index 0000000..2bd6ce8 --- /dev/null +++ b/Infra/src/test/java/com/example/config/InfraIntegrateProfileResolver.java @@ -0,0 +1,12 @@ +package com.example.config; + +import org.springframework.test.context.ActiveProfilesResolver; + +public class InfraIntegrateProfileResolver implements ActiveProfilesResolver { + + @Override + public String[] resolve(Class testClass) { + // some code to find out your active profiles + return new String[] { "local","infra","core"}; + } +} \ No newline at end of file diff --git a/Infra/src/test/java/com/example/config/InfraIntegrateTestConfig.java b/Infra/src/test/java/com/example/config/InfraIntegrateTestConfig.java new file mode 100644 index 0000000..41e77fd --- /dev/null +++ b/Infra/src/test/java/com/example/config/InfraIntegrateTestConfig.java @@ -0,0 +1,11 @@ +package com.example.config; + +import com.example.CoreApplication; +import com.example.InfraApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan(basePackageClasses = {InfraApplication.class, CoreApplication.class}) +public class InfraIntegrateTestConfig { +} diff --git a/Infra/src/test/java/com/example/oauth/tmdb/client/TmdbClientTest.java b/Infra/src/test/java/com/example/oauth/tmdb/client/TmdbClientTest.java new file mode 100644 index 0000000..acec6c0 --- /dev/null +++ b/Infra/src/test/java/com/example/oauth/tmdb/client/TmdbClientTest.java @@ -0,0 +1,104 @@ +package com.example.oauth.tmdb.client; + +import com.example.config.InfraIntegrateProfileResolver; +import com.example.config.InfraIntegrateTestConfig; +import com.example.oauth.tmdb.dto.TdmbResponseDto; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import feign.Response; +import net.minidev.json.JSONObject; +import okhttp3.ResponseBody; +import org.apache.tomcat.util.json.JSONParser; +import org.apache.tomcat.util.json.ParseException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureMockRestServiceServer; +import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.util.ResourceUtils; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.springframework.test.util.AssertionErrors.*; + +@SpringBootTest(classes = InfraIntegrateTestConfig.class) +@AutoConfigureWireMock(port=0) +@ActiveProfiles(resolver = InfraIntegrateProfileResolver.class) +@TestPropertySource(properties = { "spring.thymeleaf.enabled=false","test.port=http://localhost:${wiremock.server.port}"}) +public class TmdbClientTest { + + @Autowired + private TmdbClient tmdbClient; + @Test + public void testGetMovieData() throws IOException, ParseException { + Path file = ResourceUtils.getFile("classpath:pamyo-response.json").toPath(); + // WireMock 설정 + stubFor(get(urlEqualTo("/openapi-data2/wisenut/search_api/search_xml2.jsp")) + .withQueryParam("collection", equalTo("kmdb_new2")) + .withQueryParam("movieId", equalTo("K")) + .withQueryParam("movieSeq", equalTo("35655")) + .withQueryParam("detail", equalTo("Y")) + .withQueryParam("ServiceKey", equalTo("33D62G26J6LHL95UV54Y")) + .willReturn(aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody(Files.readAllBytes(file)))); + + // 테스트할 Feign Client 메소드 호출 + try { + + String mmovieData = tmdbClient.getMovieData("K", "35655", "Y","a"); + System.out.println(mmovieData); + + JsonParser jsonParser = new JsonParser(); + + //3. To Object + + //값 처리하는 로직 + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(mmovieData); + //4. To JsonObject + + JsonNode movieData = rootNode.path("Data").path(0).path("Result").path(0); + + + String title = movieData.path("title").asText(); + title = title.trim(); + String directorNm = movieData.path("directors").path("director").path(0).path("directorNm").asText(); + String plotText = movieData.path("plots").path("plot").path(0).path("plotText").asText(); + String firstPosterUrl = movieData.path("posters").asText().split("\\|")[0]; + //System.out.println(tes); + + System.out.println(title +directorNm +plotText+ firstPosterUrl); + // 응답이 예상대로 받아졌는지 검증합니다. + //assertNotNull("check",tes); + }catch (Exception e) { + System.out.println(e.getMessage()); + } + //assertEquals("right",test.size(),1); + + } +} \ No newline at end of file diff --git a/Infra/src/test/resources/pamyo-response.json b/Infra/src/test/resources/pamyo-response.json new file mode 100644 index 0000000..4821912 --- /dev/null +++ b/Infra/src/test/resources/pamyo-response.json @@ -0,0 +1,600 @@ +{ + "Query": "", + "KMAQuery": "", + "TotalCount": 1, + "Data": [ + { + "CollName": "kmdb_new2", + "TotalCount": 1, + "Count": 1, + "Result": [ + { + "DOCID": "K35655", + "movieId": "K", + "movieSeq": "35655", + "title": " 파묘", + "titleEng": "Exhuma (Pa-myo)", + "titleOrg": "", + "titleEtc": "파묘^破墓^Exhuma (Pa-myo)", + "prodYear": "2024", + "directors": { + "director": [ + { + "directorNm": "장재현", + "directorEnNm": "JANG Jae-hyun", + "directorId": "00184046" + } + ] + }, + "actors": { + "actor": [ + { + "actorNm": "최민식", + "actorEnNm": "Choi Min-sik", + "actorId": "00001042" + }, + { + "actorNm": "김고은", + "actorEnNm": "Kim Ko-eun", + "actorId": "00165913" + }, + { + "actorNm": "유해진", + "actorEnNm": "Yoo Hae-jin", + "actorId": "00013060" + }, + { + "actorNm": "이도현", + "actorEnNm": "Lee Dohyeon", + "actorId": "00219422" + }, + { + "actorNm": "은수", + "actorEnNm": "Eunsu", + "actorId": "00229539" + }, + { + "actorNm": "김민준", + "actorEnNm": "Kim Min-jun", + "actorId": "00011224" + }, + { + "actorNm": "김병오", + "actorEnNm": "", + "actorId": "" + }, + { + "actorNm": "코야마 리키야", + "actorEnNm": "Rikiya Koyama", + "actorId": "00156371" + }, + { + "actorNm": "최낙윤", + "actorEnNm": "Choe Nak-yun", + "actorId": "00144858" + }, + { + "actorNm": "김재철", + "actorEnNm": "Kim Jae-cheol", + "actorId": "00040812" + }, + { + "actorNm": "김지안", + "actorEnNm": "Kim Ji An", + "actorId": "00201891" + }, + { + "actorNm": "김선영", + "actorEnNm": "Kim Sun-young", + "actorId": "00177554" + }, + { + "actorNm": "이종구", + "actorEnNm": "Lee Jong-gu", + "actorId": "00135765" + }, + { + "actorNm": "김태준", + "actorEnNm": "Kim Taejun", + "actorId": "00032083" + }, + { + "actorNm": "정윤하", + "actorEnNm": "Yunha Jung", + "actorId": "00209549" + } + ] + }, + "nation": "대한민국", + "company": "㈜쇼박스,㈜파인타운 프로덕션", + "plots": { + "plot": [ + { + "plotLang": "한국어", + "plotText": "미국 LA, 거액의 의뢰를 받은 무당 ‘화림’(김고은)과 ‘봉길’(이도현)은 기이한 병이 대물림되는 집안의 장손을 만난다.조상의 묫자리가 화근임을 알아챈 ‘화림’은 이장을 권하고, 돈 냄새를 맡은 최고의 풍수사 ‘상덕’(최민식)과 장의사 ‘영근’(유해진)이 합류한다.“전부 잘 알 거야… 묘 하나 잘못 건들면 어떻게 되는지”절대 사람이 묻힐 수 없는 악지에 자리한 기이한 묘.‘상덕’은 불길한 기운을 느끼고 제안을 거절하지만, ‘화림’의 설득으로 결국 파묘가 시작되고…나와서는 안될 것이 나왔다." + }, + { + "plotLang": "영어", + "plotText": "Young shaman Hwa-rim and her partner and co-medium Bong-gil respond to a call for help from the USA, where the wealthy Park family, Korean exiles, is plagued by irritations: something is wrong with the family’s descendants and the head of the family himself is hearing screams. The duo accept the job – after all, it’s well paid – and, along with a feng shui expert and an undertaker, they start to exhume the ancestors’ grave in the north of Gangwon-do province. In the process, something escapes from the coffin, people die, others prove to be obsessed and the real problems haven’t even started yet." + } + ] + }, + "runtime": "134", + "rating": "15세관람가", + "genre": "공포,미스터리", + "kmdbUrl": "https://www.kmdb.or.kr/db/kor/detail/movie/K/35655", + "type": "극영화", + "use": "극장용", + "episodes": "", + "ratedYn": "Y", + "repRatDate": "20240129", + "repRlsDate": "20240222", + "ratings": { + "rating": [ + { + "ratingMain": "Y||0", + "ratingDate": "20240129||20240205", + "ratingNo": "2024-MF00277||2024-MF00353", + "ratingGrade": "15세관람가||15세관람가", + "releaseDate": "20240222||20240222", + "runtime": "134||134" + } + ] + }, + "keywords": "오컬트", + "posters": "http://file.koreafilm.or.kr/thm/02/99/18/33/tn_DPK021733.jpg|http://file.koreafilm.or.kr/thm/02/99/18/34/tn_DPK021758.jpg|http://file.koreafilm.or.kr/thm/02/99/18/32/tn_DPK021667.jpg|http://file.koreafilm.or.kr/thm/02/99/18/32/tn_DPK021668.jpg|http://file.koreafilm.or.kr/thm/02/99/18/31/tn_DPK021655.jpg|http://file.koreafilm.or.kr/thm/02/99/18/35/tn_DPK021794.jpg|http://file.koreafilm.or.kr/thm/02/99/18/35/tn_DPK021795.jpg|http://file.koreafilm.or.kr/thm/02/99/18/36/tn_DPK021828.jpg|http://file.koreafilm.or.kr/thm/02/99/18/36/tn_DPK021829.jpg|http://file.koreafilm.or.kr/thm/02/99/18/33/tn_DPK021700.jpg|http://file.koreafilm.or.kr/thm/02/99/18/33/tn_DPK021701.jpg|http://file.koreafilm.or.kr/thm/02/99/18/33/tn_DPK021702.jpg|http://file.koreafilm.or.kr/thm/02/99/18/33/tn_DPK021703.jpg|http://file.koreafilm.or.kr/thm/02/99/18/30/tn_DPK021616.jpg|http://file.koreafilm.or.kr/thm/02/99/18/30/tn_DPK021617.jpg|http://file.koreafilm.or.kr/thm/02/99/18/30/tn_DPK021618.jpg|http://file.koreafilm.or.kr/thm/02/99/18/30/tn_DPK021619.jpg|http://file.koreafilm.or.kr/thm/02/99/18/30/tn_DPK021620.jpg|http://file.koreafilm.or.kr/thm/02/99/18/34/tn_DPK021757.jpg|http://file.koreafilm.or.kr/thm/02/99/18/32/tn_DPK021665.jpg|http://file.koreafilm.or.kr/thm/02/99/18/32/tn_DPK021666.jpg", + "stlls": "http://file.koreafilm.or.kr/thm/01/copy/00/66/16/tn_DST834584.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/16/tn_DST834585.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/16/tn_DST834586.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/16/tn_DST834587.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/49/tn_DST837824.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/49/tn_DST837825.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/49/tn_DST837826.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/61/tn_DST839012.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/62/tn_DST839135.jpg|http://file.koreafilm.or.kr/thm/01/copy/00/66/62/tn_DST839136.jpg", + "staffs": { + "staff": [ + { + "staffNm": "장재현", + "staffEnNm": "JANG Jae-hyun", + "staffRoleGroup": "감독", + "staffRole": "", + "staffEtc": "", + "staffId": "00184046" + }, + { + "staffNm": "장재현", + "staffEnNm": "JANG Jae-hyun", + "staffRoleGroup": "각본", + "staffRole": "", + "staffEtc": "", + "staffId": "00184046" + }, + { + "staffNm": "최민식", + "staffEnNm": "Choi Min-sik", + "staffRoleGroup": "출연", + "staffRole": "상덕", + "staffEtc": "", + "staffId": "00001042" + }, + { + "staffNm": "김고은", + "staffEnNm": "Kim Ko-eun", + "staffRoleGroup": "출연", + "staffRole": "화림", + "staffEtc": "", + "staffId": "00165913" + }, + { + "staffNm": "유해진", + "staffEnNm": "Yoo Hae-jin", + "staffRoleGroup": "출연", + "staffRole": "영근", + "staffEtc": "", + "staffId": "00013060" + }, + { + "staffNm": "이도현", + "staffEnNm": "Lee Dohyeon", + "staffRoleGroup": "출연", + "staffRole": "봉길", + "staffEtc": "", + "staffId": "00219422" + }, + { + "staffNm": "은수", + "staffEnNm": "Eunsu", + "staffRoleGroup": "출연", + "staffRole": "김상덕 딸, 연희", + "staffEtc": "", + "staffId": "00229539" + }, + { + "staffNm": "김민준", + "staffEnNm": "Kim Min-jun", + "staffRoleGroup": "출연", + "staffRole": "험한 것", + "staffEtc": "", + "staffId": "00011224" + }, + { + "staffNm": "김병오", + "staffEnNm": "", + "staffRoleGroup": "출연", + "staffRole": "험한 것", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "코야마 리키야", + "staffEnNm": "Rikiya Koyama", + "staffRoleGroup": "출연", + "staffRole": "험한 것 목소리", + "staffEtc": "", + "staffId": "00156371" + }, + { + "staffNm": "최낙윤", + "staffEnNm": "Choe Nak-yun", + "staffRoleGroup": "출연", + "staffRole": "험한 것 목소리", + "staffEtc": "", + "staffId": "00144858" + }, + { + "staffNm": "김재철", + "staffEnNm": "Kim Jae-cheol", + "staffRoleGroup": "출연", + "staffRole": "박지용", + "staffEtc": "", + "staffId": "00040812" + }, + { + "staffNm": "김지안", + "staffEnNm": "Kim Ji An", + "staffRoleGroup": "출연", + "staffRole": "박자혜", + "staffEtc": "", + "staffId": "00201891" + }, + { + "staffNm": "김선영", + "staffEnNm": "Kim Sun-young", + "staffRoleGroup": "출연", + "staffRole": "오광심", + "staffEtc": "", + "staffId": "00177554" + }, + { + "staffNm": "이종구", + "staffEnNm": "Lee Jong-gu", + "staffRoleGroup": "출연", + "staffRole": "보국사 보살", + "staffEtc": "", + "staffId": "00135765" + }, + { + "staffNm": "김태준", + "staffEnNm": "Kim Taejun", + "staffRoleGroup": "출연", + "staffRole": "창민", + "staffEtc": "", + "staffId": "00032083" + }, + { + "staffNm": "정윤하", + "staffEnNm": "Yunha Jung", + "staffRoleGroup": "출연", + "staffRole": "박지용 아내", + "staffEtc": "", + "staffId": "00209549" + }, + { + "staffNm": "김영민", + "staffEnNm": "Kim Young-min", + "staffRoleGroup": "프로듀서", + "staffRole": "총괄프로듀서", + "staffEtc": "", + "staffId": "00077878" + }, + { + "staffNm": "권지용", + "staffEnNm": "", + "staffRoleGroup": "프로듀서", + "staffRole": "", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "박형진", + "staffEnNm": "", + "staffRoleGroup": "프로듀서", + "staffRole": "", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "이모개", + "staffEnNm": "Lee Mo-gae", + "staffRoleGroup": "촬영", + "staffRole": "", + "staffEtc": "", + "staffId": "00015511" + }, + { + "staffNm": "이성환", + "staffEnNm": "Lee Seong-hwan", + "staffRoleGroup": "조명", + "staffRole": "", + "staffEtc": "", + "staffId": "00007483" + }, + { + "staffNm": "정병진", + "staffEnNm": "Jung Byung-jin", + "staffRoleGroup": "편집", + "staffRole": "", + "staffEtc": "", + "staffId": "00133046" + }, + { + "staffNm": "김태성", + "staffEnNm": "Kim Tae-seong", + "staffRoleGroup": "음악", + "staffRole": "", + "staffEtc": "", + "staffId": "00033243" + }, + { + "staffNm": "서성경", + "staffEnNm": "Seo Seong-gyeong", + "staffRoleGroup": "미술", + "staffRole": "", + "staffEtc": "", + "staffId": "00166264" + }, + { + "staffNm": "정인철", + "staffEnNm": "Jeong In-cheol", + "staffRoleGroup": "세트", + "staffRole": "", + "staffEtc": "", + "staffId": "00185380" + }, + { + "staffNm": "박준용", + "staffEnNm": "Park Jun-yong", + "staffRoleGroup": "소품", + "staffRole": "", + "staffEtc": "", + "staffId": "00169449" + }, + { + "staffNm": "유청", + "staffEnNm": "Yu Cheong", + "staffRoleGroup": "소품", + "staffRole": "", + "staffEtc": "", + "staffId": "00007696" + }, + { + "staffNm": "최윤선", + "staffEnNm": "Choe Yun-seon", + "staffRoleGroup": "의상", + "staffRole": "", + "staffEtc": "", + "staffId": "00009521" + }, + { + "staffNm": "이은주", + "staffEnNm": "Lee Eun-Ju", + "staffRoleGroup": "분장", + "staffRole": "", + "staffEtc": "", + "staffId": "00010035" + }, + { + "staffNm": "황효균", + "staffEnNm": "Hwang Hyo-gyun", + "staffRoleGroup": "특수분장", + "staffRole": "", + "staffEtc": "", + "staffId": "00009046" + }, + { + "staffNm": "이희은", + "staffEnNm": "Lee Hui-eun", + "staffRoleGroup": "특수분장", + "staffRole": "", + "staffEtc": "", + "staffId": "00009050" + }, + { + "staffNm": "곽태용", + "staffEnNm": "Gwak Tae-yong", + "staffRoleGroup": "특수분장", + "staffRole": "", + "staffEtc": "", + "staffId": "00007996" + }, + { + "staffNm": "김호식", + "staffEnNm": "Kim Ho-sik", + "staffRoleGroup": "특수분장", + "staffRole": "", + "staffEtc": "", + "staffId": "00166512" + }, + { + "staffNm": "구종률", + "staffEnNm": "Koo Jong-ryul", + "staffRoleGroup": "동시녹음", + "staffRole": "", + "staffEtc": "", + "staffId": "00153329" + }, + { + "staffNm": "김병인", + "staffEnNm": "Kim Byeong-in", + "staffRoleGroup": "사운드(음향)", + "staffRole": "", + "staffEtc": "", + "staffId": "00162836" + }, + { + "staffNm": "도광일", + "staffEnNm": "Do Gwang-il", + "staffRoleGroup": "특수효과", + "staffRole": "", + "staffEtc": "", + "staffId": "00008781" + }, + { + "staffNm": "도광섭", + "staffEnNm": "Do Gwang-seop", + "staffRoleGroup": "특수효과", + "staffRole": "", + "staffEtc": "", + "staffId": "00044275" + }, + { + "staffNm": "김신철", + "staffEnNm": "Kim Shin Cheol", + "staffRoleGroup": "시각효과", + "staffRole": "", + "staffEtc": "", + "staffId": "00201009" + }, + { + "staffNm": "손승현", + "staffEnNm": "Son Seung-hyeon", + "staffRoleGroup": "시각효과", + "staffRole": "", + "staffEtc": "", + "staffId": "00018987" + }, + { + "staffNm": "정윤헌", + "staffEnNm": "Jeong Yoonhun", + "staffRoleGroup": "무술감독", + "staffRole": "", + "staffEtc": "", + "staffId": "00147375" + }, + { + "staffNm": "문광식", + "staffEnNm": "Moon Kwang Sik", + "staffRoleGroup": "무술감독", + "staffRole": "", + "staffEtc": "", + "staffId": "00200827" + }, + { + "staffNm": "박진영", + "staffEnNm": "Park Jin-young", + "staffRoleGroup": "색보정", + "staffRole": "", + "staffEtc": "", + "staffId": "00150353" + }, + { + "staffNm": "홍정원", + "staffEnNm": "Hong Jeong-won", + "staffRoleGroup": "촬영팀", + "staffRole": "키그립", + "staffEtc": "", + "staffId": "00166572" + }, + { + "staffNm": "㈜쇼박스", + "staffEnNm": "Showbox", + "staffRoleGroup": "제작사", + "staffRole": "", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "㈜파인타운 프로덕션", + "staffEnNm": "", + "staffRoleGroup": "제작사", + "staffRole": "", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "㈜MCMC", + "staffEnNm": "", + "staffRoleGroup": "공동제작사", + "staffRole": "", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "㈜쇼박스", + "staffEnNm": "Showbox", + "staffRoleGroup": "투자사", + "staffRole": "", + "staffEtc": "", + "staffId": "" + }, + { + "staffNm": "㈜쇼박스", + "staffEnNm": "Showbox", + "staffRoleGroup": "배급사", + "staffRole": "", + "staffEtc": "", + "staffId": "" + } + ] + }, + "vods": { + "vod": [ + { + "vodClass": "파묘 [1차예고편]", + "vodUrl": "https://www.kmdb.or.kr/trailer/trailerPlayPop?pFileNm=MK060579_P02.mp4" + }, + { + "vodClass": "파묘 [2차예고편]", + "vodUrl": "https://www.kmdb.or.kr/trailer/trailerPlayPop?pFileNm=MK060763_P02.mp4" + } + ] + }, + "openThtr": "", + "stat": [ + { + "screenArea": "", + "screenCnt": "", + "salesAcc": "", + "audiAcc": "", + "statSouce": "", + "statDate": "" + } + ], + "screenArea": "", + "screenCnt": "", + "salesAcc": "", + "audiAcc": "", + "statSouce": "", + "statDate": "", + "themeSong": "", + "soundtrack": "", + "fLocation": "", + "Awards1": "", + "Awards2": "", + "regDate": "20231129", + "modDate": "20240315", + "Codes": { + "Code": [ + { + "CodeNm": "FIMS", + "CodeNo": "20234675" + } + ] + }, + "CommCodes": { + "CommCode": [ + { + "CodeNm": "FIMS", + "CodeNo": "20234675" + } + ] + }, + "ALIAS": "srcKmdb2" + } + ] + } + ] +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index d892ac7..5d6d71e 100644 --- a/build.gradle +++ b/build.gradle @@ -35,10 +35,17 @@ subprojects { testImplementation 'org.springframework.boot:spring-boot-starter-test' testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation "org.springframework.cloud:spring-cloud-contract-wiremock:4.1.1" + testRuntimeOnly 'com.h2database:h2' /* fcm */ implementation 'com.google.firebase:firebase-admin:9.2.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0' + + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' } repositories {