안녕하세요. express 에서 rest 방식에서는 API를 생성할 때, get이나 post 등의 메소드 방식을 정하고 주소를 지정하여 프론트엔드에서 fetch 나 axois 함수에 주소를 기입하여 API 요청을 했었습니다. graphQL 방식의 nestjs에서는 프론트엔드에서 API 요청을 보낼 때 어떤 방식으로 하나요? 따로 주소 같은 것을 지정해주지는 않는 것같은데.. 똑같이 fetch 나 axois 함수를 통해 요청을 하는 건가요?
[개정판 2023-11-27] Spring Boot 3.x 를 이용한 RESTful Web Services 개발
spring 버전으로 springfox대신 springdoc을 사용했습니다. 따로 코드상 오류는 안나지만 서버를 실행시키면 에러가 뜹니다. SwaggerConfig.java 에러내용입니다. 023-08-04 18:21:26.795 ERROR 8588 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.example.demo.DemoApplication]; nested exception is java.lang.IllegalArgumentException: Unresolvable class definition for class [org.springdoc.core.conditions.MultipleOpenApiSupportCondition] at org.springframework.context.annotation.ConfigurationClassParser.processImports( ConfigurationClassParser.java:609 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassParser.access$800( ConfigurationClassParser.java:110 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1( ConfigurationClassParser.java:812 ) ~[spring-context-5.3.25.jar:5.3.25] at java.base/java.util.ArrayList.forEach( ArrayList.java:1541 ) ~[na:na] at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports( ConfigurationClassParser.java:809 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process( ConfigurationClassParser.java:780 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassParser.parse( ConfigurationClassParser.java:192 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions( ConfigurationClassPostProcessor.java:331 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry( ConfigurationClassPostProcessor.java:247 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.support .PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors( PostProcessorRegistrationDelegate.java:311 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.support .PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors( PostProcessorRegistrationDelegate.java:112 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.support .AbstractApplicationContext.invokeBeanFactoryPostProcessors( AbstractApplicationContext.java:746 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.support .AbstractApplicationContext.refresh( AbstractApplicationContext.java:564 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh( ServletWebServerApplicationContext.java:147 ) ~[spring-boot-2.7.8.jar:2.7.8] at org.springframework.boot.SpringApplication.refresh( SpringApplication.java:731 ) ~[spring-boot-2.7.8.jar:2.7.8] at org.springframework.boot.SpringApplication.refreshContext( SpringApplication.java:408 ) ~[spring-boot-2.7.8.jar:2.7.8] at org.springframework.boot.SpringApplication.run ( SpringApplication.java:307 ) ~[spring-boot-2.7.8.jar:2.7.8] at org.springframework.boot.SpringApplication.run ( SpringApplication.java:1303 ) ~[spring-boot-2.7.8.jar:2.7.8] at org.springframework.boot.SpringApplication.run ( SpringApplication.java:1292 ) ~[spring-boot-2.7.8.jar:2.7.8] at com.example.demo.DemoApplication.main( DemoApplication.java:15 ) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke( NativeMethodAccessorImpl.java:62 ) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke( DelegatingMethodAccessorImpl.java:43 ) ~[na:na] at java.base/java.lang.reflect.Method.invoke( Method.java:566 ) ~[na:na] at org.springframework.boot.devtools.restart.RestartLauncher.run ( RestartLauncher.java:49 ) ~[spring-boot-devtools-2.7.8.jar:2.7.8] Caused by: java.lang.IllegalArgumentException: Unresolvable class definition for class [org.springdoc.core.conditions.MultipleOpenApiSupportCondition] at org.springframework.util.ClassUtils.resolveClassName( ClassUtils.java:331 ) ~[spring-core-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConditionEvaluator.getCondition( ConditionEvaluator.java:124 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConditionEvaluator.shouldSkip( ConditionEvaluator.java:96 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass( ConfigurationClassParser.java:225 ) ~[spring-context-5.3.25.jar:5.3.25] at org.springframework.context.annotation.ConfigurationClassParser.processImports( ConfigurationClassParser.java:599 ) ~[spring-context-5.3.25.jar:5.3.25] ... 24 common frames omitted Caused by: java.lang.UnsupportedClassVersionError: org/springdoc/core/conditions/MultipleOpenApiSupportCondition has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0 at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na] at java.base/java.lang.ClassLoader.defineClass( ClassLoader.java:1016 ) ~[na:na] at java.base/java.security.SecureClassLoader.defineClass( SecureClassLoader.java:174 ) ~[na:na] at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass( BuiltinClassLoader.java:800 ) ~[na:na] at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull( BuiltinClassLoader.java:698 ) ~[na:na] at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull( BuiltinClassLoader.java:621 ) ~[na:na] at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass( BuiltinClassLoader.java:579 ) ~[na:na] at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass( ClassLoaders.java:178 ) ~[na:na] at java.base/java.lang.ClassLoader.loadClass( ClassLoader.java:521 ) ~[na:na] at java.base/java.lang.Class.forName0(Native Method) ~[na:na] at java.base/java.lang.Class.forName( Class.java:398 ) ~[na:na] at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass( RestartClassLoader.java:145 ) ~[spring-boot-devtools-2.7.8.jar:2.7.8] at java.base/java.lang.ClassLoader.loadClass( ClassLoader.java:521 ) ~[na:na] at java.base/java.lang.Class.forName0(Native Method) ~[na:na] at java.base/java.lang.Class.forName( Class.java:398 ) ~[na:na] at org.springframework.util.ClassUtils.forName( ClassUtils.java:284 ) ~[spring-core-5.3.25.jar:5.3.25] at org.springframework.util.ClassUtils.resolveClassName( ClassUtils.java:324 ) ~[spring-core-5.3.25.jar:5.3.25] ... 28 common frames omitted
안녕하세요 원두멘토님ㅎㅎ 이런 에러가 계속 떠서 docs와 스텍오버플로우에 검색을 해보니 Passport-jwt를 import하는 것이 아닌 passport-local을 import하는 것이라해서 했더니 다행히 실행이 됩니다. 수업내용에서는 passport-jwt로만 해도 작동이 잘되는 것 같은데 왜 이런 문제가 생기는 것일까요? 그리고 passport-local로 하면 실제 배포에서는 사용 못하는 것이 아닌지 질문을 남깁니다. p.s 재작년에 코캠2기 수료하고 백엔드를 많이 기다렸는데 이렇게 인프런에서 다시 인강으로 뵙게 되서 너무 반갑습니다
[개정판 2023-11-27] Spring Boot 3.x 를 이용한 RESTful Web Services 개발
Spring 3 이상부터는 더 이상 Springfox가 아닌 SpringDoc으로 해야 Swagger 설정을 해줄 수 있습니다. 자세한 내용은 제가 참조한 아래 레퍼런스 확인해보시면 좋을 거 같아요 https://springdoc.org/ https://colabear754.tistory.com/99 https://velog.io/@kjgi73k/Springboot3%EC%97%90-Swagger3%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0 아래는 참고하여 작성한 코드입니다. @Configuration public class SwaggerConfig { @Bean public OpenAPI openAPI(){ return new OpenAPI() .components(new Components()) .info(apiInfo()); } private Info apiInfo() { return new Info() .title("Springdoc 테스트") .description("Springdoc을 사용한 Swagger UI 테스트") .version("1.0.0"); } } @RestController @RequestMapping("/swagger") public class SwaggerController { @Operation(summary = "문자열 반복", description = "파라미터로 받은 문자열을 2번 반복합니다.") @Parameter(name = "str", description = "2번 반복할 문자열") @GetMapping("/returnStr") public String returnStr(@RequestParam String str) { return str + "\n" + str; } @GetMapping("/example") public String example() { return "예시 API"; } @Hidden @GetMapping("/ignore") public String ignore() { return "무시되는 API"; } } NOTE: springdoc.swagger-ui.path 에서 swagger url 기본 경로를 설정합니다. 저는 아래와 같이 /swagger로 접속하게끔 했습니다. # SPRINGDOC springdoc.packages-to-scan=com.example.dateanu.rest_api springdoc.default-consumes-media-type= application/json;charset=UTF-8 springdoc.default-produces-media-type= application/json;charset=UTF-8 springdoc.swagger-ui.path=/swagger springdoc.swagger-ui.disable-swagger-default-url=true springdoc.swagger-ui.display-request-duration=true springdoc.swagger-ui.operations-sorter=alpha
구글 앱 비밀번호 설정 떄문에 막힙니다 2단계 인증했고 앱 비밀번호 생성해서 그걸 구글 비밀번호로 바꾸고 선생님 코드를 잘 따라해서 적었는데 Error: Invalid login: 535-5.7.8 Username and Password not accepted. 라고 에러 납니다 혹시 방법 없을까요?
선생님께서는 type Mutation { createBoard(createBoardInput: CreateBoardInput!):String } 밸류 CretaBoardInput !을 그룹으로 묶어서 input CreateBoardInput{ writer:String title:String contents: String } 이렇게 나타내셨는데 , 그런데 키 createBoardInput은 뭘 나타내는 건가요? 혹시, 객체를 그룹으로 묶으면 키,밸류를 똑같은 이름으로 지어야 하나요>?
nestjs + mongoose 조합이지만, https://velog.io/@modolee/mongodb-document-to-javascript-object 와 같이 find()가 리턴하는 인스턴스 내부가 `_doc` 처럼 되어 있었습니다. 저는 user.age 이렇게 사용하고 싶은데, user 밑에 age 라는 변수도 없고, 꼭 .toObject()를 해야 하나요? 아직 이 부분에 수수께끼가 풀리지 않았습니다. 설명해 주실 수 있나요?
안녕하세요. 섹션 27의 8-5 CURD API 강의를 듣다가 문득 궁금한 점이 생겨 질문드립니다. 해당 강의에서는 boards.resolver.ts 파일에 fetchBoards함수와 createBoards 함수를 정의하기 위해 board.entity.ts 파일, create-board.input.ts 파일, boards-service.interface.ts 파일을 아래와 같이 만드셨습니다. 아래 세 파일은 구조가 거의 동일한데, 왜 이렇게 같은 구조의 파일을 세개나 만들어서 타입스크립트 타입 정의에 사용하는 것인지 이해가 가질 않습니다. 그리고 어떤 것은 class로 정의하여 타입스크릡트에 사용하고, 어떤 것은 interface로 정의하여 타입스크립트로 사용하는데 어떤 차이인지 질문 드립니다. // board.entity.ts 파일 import { Field, Int, ObjectType } from '@nestjs/graphql'; import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() @ObjectType() export class Board { @PrimaryGeneratedColumn('increment') @Field(() => Int) num: number; @Column() @Field(() => String) title: string; @Column() @Field(() => String) contents: string; @Column() @Field(() => String) writer: string; } // create-board.input.ts 파일 import { Field, InputType } from '@nestjs/graphql'; @InputType() export class CreateBoardInput { @Field(() => String) writer: string; @Field(() => String) title: string; @Field(() => String) contents: string; } // boards-service.interface.ts 파일 import { CreateBoardInput } from '../dto/create-board.input'; export interface IBoardsServiceCreate { createBoardInput: CreateBoardInput; }