구글 앱 비밀번호 설정 떄문에 막힙니다 2단계 인증했고 앱 비밀번호 생성해서 그걸 구글 비밀번호로 바꾸고 선생님 코드를 잘 따라해서 적었는데 Error: Invalid login: 535-5.7.8 Username and Password not accepted. 라고 에러 납니다 혹시 방법 없을까요?
안녕하세요~ 'Mockito로 Stubbing하기' 강의를 듣고 궁금한 내용이 있습니다. 메일을 전송하는 부분을 stubbing하셨는데요. 보통 어떤 경우에 실무에서 stubbing하여 테스트를 작성하나요? 감이 잘 안잡히네요.. 제가 이해한건 아래처럼 이해했습니다. sendMail 메서드에 대한 테스트는 '메일만 전송'하는 테스트를 작성한다. sendOrderStatisticsMail 테스트를 작성하는 도중 sendMail 부분은 이미 '1번'에서 따로 테스트 케이스를 작성했으므로 넘어가도 무방하다. 따라서 여기는 stubbing하여 간단하게 넘어간다. 즉, 이번 예시에서는 메일이지만 확장하여 생각해본다면 외부 api를 호출하는 경우에는 stubbing을 진행한다.
선생님께서는 type Mutation { createBoard(createBoardInput: CreateBoardInput!):String } 밸류 CretaBoardInput !을 그룹으로 묶어서 input CreateBoardInput{ writer:String title:String contents: String } 이렇게 나타내셨는데 , 그런데 키 createBoardInput은 뭘 나타내는 건가요? 혹시, 객체를 그룹으로 묶으면 키,밸류를 똑같은 이름으로 지어야 하나요>?
안녕하세요~ 좋은 강의 잘 듣고 있습니다. 질문이 하나 있는데요. 컨트롤러에서 @Valid로 필드를 검증이 되지 않으면 공통 예외처리에 걸려서 응답처리가 되고, 정상적으로 필드가 검증이 됐다면 서비스단에 로직을 처리할텐데요~ 서비스단에서는 다시 필드를 검증할 필요가 없을까요? 컨트롤러, 서비스를 나눠서 테스트를 작성하다보니 서비스 단에서도 검증을 해야하나 궁금합니다. 실무에서는 어떻게 보통 어떻게 진행되나요?
안녕하세요. 섹션 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; }
안녕하세요 좋은 강의 덕분에 테스트에 대해 많이 알게 되었습니다 :) 스프링 시큐리티를 포함한 테스트와 관련해서 질문을 드려보고자 글을 쓰게 되었습니다. (강의에서는 스프링 시큐리티를 사용하지 않지만 테스트와 관련해서 물어볼 곳이 마땅치 않아 이 곳에 글을 남기는 점 양해해주시면 감사하겠습니다) @RequiredArgsConstructor @RestController @RequestMapping("/api") @Slf4j public class AuthController { private final AuthService authService; @PostMapping("/auth/sign-up") public String signUp(@RequestBody SignUpRequest request) { authService.signUp(request); return "회원가입 성공"; } } 현재 회원가입을 하는 컨트롤러의 메서드는 다음과 같습니다. @Service @RequiredArgsConstructor @Slf4j @Transactional(readOnly = true) public class AuthService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; @Transactional public void signUp(SignUpRequest request) { if (userRepository.findByEmail(request.getEmail()).isPresent()) { throw new AlreadyExistException("이미 존재하는 이메일입니다."); } if (userRepository.findByNickname(request.getNickname()).isPresent()) { throw new AlreadyExistException("이미 존재하는 닉네임입니다."); } User user = User.builder() .name(request.getName()) .email(request.getEmail()) .password(request.getPassword()) .nickname(request.getNickname()) .role(UserRole.USER) .build(); user.passwordEncode(passwordEncoder); userRepository.save(user); } } 그리고 실제 회원가입이 진행되는 로직은 이렇습니다. 그리고 제가 WebMvcTest 어노테이션을 사용해서 테스트 코드를 작성한 건 다음과 같습니다. @ActiveProfiles("test") @AutoConfigureMockMvc @WebMvcTest(AuthController.class) class AuthControllerTestWithWebMvcTest { @Autowired protected MockMvc mockMvc; @Autowired protected ObjectMapper objectMapper; @MockBean private AuthService authService; @MockBean private UserRepository userRepository; @DisplayName("회원가입 한다.") @CustomMockUser // 회원가입을 해야되는데 인증이 필요하다? -> 논리적으로 말이 안 됨 -> 올바른 테스트? @Test void signUp() throws Exception { // given SignUpRequest request = SignUpRequest.builder() .name("zun") .email("zun@test.com") .password("12345") .nickname("zunny") .build(); // then mockMvc.perform(post("/api/auth/sign-up") .content(objectMapper.writeValueAsString(request)) .contentType(APPLICATION_JSON) .with(csrf()) ) .andExpect(status().isOk()) .andDo(print()); } } 보시다시피 회원가입 API에 요청이 제대로 되는지를 확인하기 위해 @CustomMockUser 라는 어노테이션을 사용하고 있습니다. (이 어노테이션은 테스트에서 인증을 처리하기 위해 만든 어노테이션입니다. 호돌맨님 강의를 참고해서 만들었습니다.) 제가 혼란스러운 부분은 테스트코드에 써놓은 주석처럼 회원가입이 제대로 되는지를 테스트하기 위한 컨트롤러 코드인데 이걸 통과시키기 위해 인증을 담당하는 @CustomMockUser를 사용하는 것이 올바른 테스트인가 하는 의문입니다. @WebMvcTest를 사용하지 않고 @SpringBootTest를 사용하면 다음처럼 할 수가 있습니다. @ActiveProfiles("test") @AutoConfigureMockMvc @SpringBootTest class AuthControllerTest { @Autowired private UserRepository userRepository; @Autowired private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; @BeforeEach void tearDown() { userRepository.deleteAllInBatch(); } @DisplayName("회원가입 한다.") @Test void signUp() throws Exception { //given SignUpRequest request = SignUpRequest.builder() .name("zun") .email("zun@test.com") .password("12345") .nickname("zunny") .build(); //expected mockMvc.perform(post("/api/auth/sign-up") .content(objectMapper.writeValueAsString(request)) .contentType(APPLICATION_JSON) ) .andExpect(status().isOk()) .andDo(print()); } } 혹시 이와 관련해서 우빈님은 어떻게 생각하시는지, 그리고 스프링 시큐리티를 사용하는 프로젝트에서는 컨트롤러 쪽 테스트를 어떻게 하는 것이 좋은지 알려주시면 감사하겠습니다!
안녕하세요 강사님, 먼저 좋은 강의 잘 듣고 있어서 감사의 말씀 드립니다. 테스트에 대한 질문은 아니지만 강의 중 작성해주신 코드에 관하여 궁금증이 생겨 질문 드립니다. 만약 문제가 되는 경우에는 삭제하도록 하겠습니다. 먼저 궁금증이 생긴 위치는 다음과 같습니다. Business Layer 테스트 (3) 강의의 31:07 Stock 엔티티의 deductQuantity 메소드에서 예외를 발생시키는 코드 public void deductQuantity(int quantity) { if (isQuantityLessThan(quantity)) { throw new IllegalArgumentException("차감할 재고 수량이 없습니다."); } this.quantity -= quantity; } [질문] 강의 중에서 deductQuantity 메소드에서 왜 isQuantityLessThan로 검증해야 하는지 설명해 주셨는데 그 부분은 이해가 되었습니다. 그런데 예외 메세지를 직접 문자열로 작성해주는 부분에서 궁금증이 생겼습니다. 스프링 빈에서 MessageSource 인터페이스를 통해 별도의 properties 파일로 예외 메세지 관리가 가능한데, 엔티티는 스프링 빈으로 등록되는 것이 아니어서 MessageSource를 통해 예외 메세지를 가져올 수 없을 것이라 생각됩니다. (그러나 deductQuantity가 호출되는 시점에서 진짜로 차감할 재고 수량이 있는지 체크하는 것도 타당하다고 생각합니다.) 그렇다면 서비스에서는 MessageSource를 통해 에러 메세지를 가져오되, 엔티티에서는 문자열로 직접 작성하는 방법밖에 없을까요? (대부분의 메세지는 properties 파일에 작성해두고, 엔티티에서만 직접 문자열로 작성하는 것이 약간 통일되지 않았다는 느낌이 들기도 하는데 어쩔 수 없는 것 같기도 합니다..) 혹시 현업에서도 MessageSource로 메세지 처리가 가능한 부분은 MessageSource를 사용하고, 그렇지 않는 부분에서는 직접 문자열로 작성하는 방식이 많이 사용되는지도 궁금합니다.
안녕하세요. 강사님께서 OrderProduct 클래스를 아래와 같이 작성하셨습니다. Order와 Product는 N:N 양방향 관계인데 이것을 피하기 위해 OrderProduct 클래스를 새로 생성하셨는데요. order 필드가 @ManyToOne이면 product 필드가 @OneToMany가 되어야 하는게 아닌가요? 그래야 N:N 구조가 되는 것 같아서요! @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class OrderProduct extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) private Order order; @ManyToOne(fetch = FetchType.LAZY) private Product product; public OrderProduct(Order order, Product product) { this.order = order; this.product = product; } }
강사님 제가 토스페이로 테스트를 진행중에 결제성공 메시지가 나오는데 db에 데이터 변경점이 없어서 확인해보니까 토큰이 만료된 상태더라구요. 토큰 만료됐을때 graphql에서는 unauthorization에러가 나왔는데 html liveServer에서는 따로 알 수 있는 방법 없을까요? 그리고 실제 서비스라면 accessToken이 만료되었을때 자동으로 refreshToken이 accessToken으로 변환되면서 고객이 느낄때는 결제 진행에 아무 무리없는 상태로 표시되나요? 테스트할때는 refreshToken도 직접 요청해서 받아오고 다시 header에 세팅하고 테스트하는거라서 이게 실제 서비스에서는 어떤 로직으로 자동화되는지 궁금합니다.
섹션 27. Nest.js 08-06 Docker 패키징 강의를 따라서 docker-compose build 를 했더니 아래와 같은 에러가 나옵니다. > [5/6] RUN yarn install: #0 2.639 yarn install v1.22.19 #0 3.290 [1/4] Resolving packages... #0 5.360 [2/4] Fetching packages... #0 66.77 info There appears to be trouble with your network connection. Retrying... #0 100.1 info There appears to be trouble with your network connection. Retrying... #0 141.5 error @graphql-tools/merge@9.0.0: The engine "node" is incompatible with this module. Expected version ">=16.0.0". Got "14.21.3" #0 141.5 error Found incompatible module. #0 141.5 info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command. ------ failed to solve: process "/bin/sh -c yarn install" did not complete successfully: exit code: 1 챗gpt 한테 물어보니까 이 오류는 @graphql-tools/merge 모듈의 버전 9.0.0이 설치되지 않았으며, 해당 모듈은 Node.js 엔진의 버전이 16.0.0 이상이어야 작동한다는 것을 의미합니다. 그러나 현재 사용 중인 Node.js 버전은 14.21.3이기 때문에 설치할 수 없다는 오류입니다. 라고 하길래 Dockerfile 의 FROM node: 14 이 부분을 FROM node: 16 으로 수정하고 다시 docker-compose build 를 했더니 에러가 안나는데 이렇게 node를 16으로 수정하고 강의를 따라가도 문제가 없을까요?
Utility 부분 강의에서 type의 Partial, Required 등을 이용하여 모든 프라퍼티를 옵션으로 바꾸거나 필수로 바꾸는 방법을 가르쳐주셨는데, 특정 하나만 옵션으로 하거나, 특정 하나만 필수인자로 바꾸는 방법은 없을까요? interface IProfile { name: string; age: number; school: string; hobby?: string; friend:? string; } 으로 정의했다고 하면 여기서 school만 옵션으로 바꾸고 싶다면, 방법이 없을까요?
안녕하세요, 좋은 강의 잘 듣고 있습니다. 강의 14분쯤에 forDisplay() 메서드를 ProductSellingType Enum 파일에서 생성을 하셨는데요. ProductService 클래스가 아닌 ProductSellingType Enum에서 생성한 이유가 있을까요? 어떠한 기준으로 생성을 하셨는지 궁금합니다. 추가적으로 이런 부분에 있어 특정 기준을 세우는 관련 글?을 읽고 싶은데 키워드 같은게 있을까요?
// 6. generic 타입 - 3 function getGeneric3<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] { return [arg3, arg2, arg1]; } const result6 = getGeneric3<string, number, boolean>("철수", 123, true); 타입명을 T1, T2, T3 에서 T, U, V 로 줄일 수 있다고 보여주셨는데 T, U, V 는 무엇의 줄인 말인가요? 그냥 의미 없는 알파벳인가요?