묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Practical Testing: 실용적인 테스트 가이드
Spring Security 를 포함한 WebMvcTest 질문드립니다.
안녕하세요 좋은 강의 덕분에 테스트에 대해 많이 알게 되었습니다 :)스프링 시큐리티를 포함한 테스트와 관련해서 질문을 드려보고자 글을 쓰게 되었습니다. (강의에서는 스프링 시큐리티를 사용하지 않지만 테스트와 관련해서 물어볼 곳이 마땅치 않아 이 곳에 글을 남기는 점 양해해주시면 감사하겠습니다)@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()); } } 혹시 이와 관련해서 우빈님은 어떻게 생각하시는지, 그리고 스프링 시큐리티를 사용하는 프로젝트에서는 컨트롤러 쪽 테스트를 어떻게 하는 것이 좋은지 알려주시면 감사하겠습니다!
-
미해결
스프링 db - 2 동작원리
해당 프로젝트에서 controller가 따로 없는 것으로 보이는데 service나 repository에 있는 메서드들의 호출이 어떻게 가능한지 궁금합니다.
-
해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진
Input.anyKey == false 를 사용하는 이유? (KeyAction == null 은 안되나요?)
InputManager 에서 마우스든,키보드든 아무키나 눌렸는지 확인할때 Input.anyKey == false 를 사용하셨는데요.(KeyAction == null) 이렇게 사용해도 같은 의미인것 같아서 혼란스러운데요. (KeyAction == null) 이 내용으로 대체해도 상관 없나요?
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 퀘스트&업적 시스템
description 생성할때 애니메이션
안녕하세요Quest Tracker 부분에서 새로운 퀘스트가 등록되거나 새로운 task가 등록될때 애니메이션을 주면서 생성하고싶은데 vertical layout group 때문에 UI가 고정되있어서 어떤식으로 해야할지 고민입니다. vertical layout group을 끄고 해볼려고했는데 content size 가 적용이 안되서 이상해보입니다. 혹시 어떤 방법이 있을까요??
-
미해결만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
2. 사용자 API 만들기 => 강의는 짧지만 백엔드 api 만드는 기본 내용 다 들어가 있음 주의하세요!!
유저 api 하나 간단히 만드는 줄 알았는데..리팩터링하고 또하고모델과 컨트롤러 파일로 만들고 (모르신다면 MVC 패턴)에러 핸들링의 기초까지 싹 다 훑어버림1시간도 안되는 강의지만조심하세요 프린이 여러분들...근데 잘보면 뒤에 계속 API를 추가하기 때문에 여기서 정신차리셔야합니다... 꺾였지만 계속 가는 마음이 중요합니다 고고
-
미해결Angular(2+) Front에서 Back까지 - Nest js를 알려면 Angular 모듈을 알아야한다!
Angular language service가 제대로 작동하지 않을때..
안녕하세요, vscode를 사용하며 따라하고 있습니다.vscode 익스텐션에서 angular language service를 깔아 사용하고 있는데, ngOnChange 같은 메소드가 자동 완성으로 뜨지 않습니다.그래서 npm으로 플러그인도 설치해보고 config파일도 바꿔봤는데 여전히 뜨지 않네요 ㅠㅠ혹시 문제가 어떤 부분일지 짐작하실 수 있을까요...
-
미해결나도코딩의 자바 기본편 - 풀코스 (20시간)
chap_04 continue 문의
int max = 20; // 최대 치킨 판매 수량 int sold = 0; // 현재 치킨 판매 수량 int noShow = 17; // 대기번호 17번 손님이 노쇼 for (int i = 1; i < 50; i++) { System.out.println(i + "번 손님, 주문하신 치킨 나왔습니다."); sold++; // 판매 처리 //손님이 없다면? (noShow) if (i == noShow) { System.out.println(i + "번 손님, 노쇼로 인해 다음 손님에게 기회가 넘어갑니다."); continue; } if (sold == max) { System.out.println("금일 재료가 모두 소진되었습니다."); break; } } System.out.println("영업을 종료합니다."); 여기서 질문이 하나 있습니다.코드에서도 for문안에 들어오면서 바로 sold부분이 증가하는데 noshow부분에서 sold가 증가하지 않아야 하는거 아닐까요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
rollback(false) 로 회원 추가 후, rollback(false) 없애고 다시 동일한 회원 추가 시
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]위의 회원가입 함수를 처음에 rollback(false)로 실행하면 데이터베이스에 Name이 "kim"인 데이터가 들어가는걸 확인했습니다. 질문1이후에 rollback(false)를 제거하고 다시 회원가입 함수를 실행하면 join에서 기존에 db에 "kim" 이라는 회원이 존재하기 때문에 예외가 발생해야 하는 것 아닌가요? 그런데 왜 정상적으로 join되었다가 롤백되는건지 궁금합니다.질문2예외가 발생한다고 예상했지만 오히려 기존의 "kim" 회원 또한 사라졌습니다. 기존의 데이터조차 사라지는 이유가 무엇인가요?. 테스트 하기 전 기존에 데이터베이스에 있던 데이터들은 롤백되어도 그대로 있어야 하는것 아닌가요? *혹시 테스트 메서드를 실행할때, 기본적으로 데이터베이스를 비우고 시작하는 건가요? 그렇다면 다시 롤백했을때 비어있는 데이터베이스로 돌아오게 되고, 그러면 위 궁금증이 해소되긴 합니다.
-
미해결10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트
[8 - P] 질문입니다
안녕하세요 선생님, 벨만 포드 사이클에 대해 이해가지 않는 부분이 있어 질문을 드립니다. https://www.acmicpc.net/source/share/34ff214ab6c944f59092bb5282b4c00e답지 31 ~ 46라인에 대해 이해가 잘 안되는 상태입니다.사이클이 발생할 수 있는 모든 here을 큐에 넣어둔 후there.first == e일 경우 돈을 무한대로 가질 수 있다고 처리하고 있는데 사이클 발생 끝 지점이 종료 노드인것이 어떻게 돈을 무한대로 가질 수 있다는 뜻이 되는지 모르겠습니다. 음의 사이클이 일어난 시작지점이 종료노드 바로 직전이었다고 한다면 종료노드에 가기 전까지 음의 사이클을 돌며 가진 돈을 줄였으면 줄였지 늘리진 못할 것 같고 음의 사이클을 타지 않더라도 벌 수 있는 돈은 마지막에 벌 수 있는 돈= (종료노드 직전까지 벌 수 있는 돈의 최대값) - (시작지점 ~ 종료지점 교통비) + (종료지점에서 번 돈)으로 한정될 것으로 보이는데 돈이 무한대가 될 수 있는 이유는 무엇인가요?
-
미해결[입문자를 위한 UE5] Part2. 언리얼 엔진 2D 게임 개발 입문
Cast To WBP_HpBar를 해주었는데 Get HpProgressBar가 없습니다.
영상 10:48초 부분입니다.먼저 WBP_HpBar에 ProgressBar를 생성해 HpProgress로 이름을 바꿔주었습니다.다음 BP_Creature의 컴포넌트에 Widget을 추가하고 Detail창에 있는 Widget Class에 WBP_HpBar을 연결해 주었습니다.그리고 Cast to해주었는데 get HpProgressBar를 꺼내 줄려고하는데 없다고 뜹니다 ㅜGet HpProgressBar가 생성이 안되는 이유가 있을까요?3번정도 반복해서 들었는데중간에 놓친부분이 있을까요?
-
미해결[코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
더티 에러
최종 실행에서 다음과 같은 에러가 뜨네요. 해결방법이 어떻게 될까요?
-
미해결AWS Certified Solutions Architect - Associate 자격증 준비하기
기간 연장 요청
회사 업무와 병행 하다 보니 기간이 부족한 것 같습니다..기간 연장 부탁 드리겠습니다! 감사합니다
-
해결됨Practical Testing: 실용적인 테스트 가이드
Stock 엔티티의 예외처리 관련하여 질문 드립니다.
안녕하세요 강사님, 먼저 좋은 강의 잘 듣고 있어서 감사의 말씀 드립니다.테스트에 대한 질문은 아니지만 강의 중 작성해주신 코드에 관하여 궁금증이 생겨 질문 드립니다.만약 문제가 되는 경우에는 삭제하도록 하겠습니다. 먼저 궁금증이 생긴 위치는 다음과 같습니다.Business Layer 테스트 (3) 강의의 31:07Stock 엔티티의 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를 사용하고, 그렇지 않는 부분에서는 직접 문자열로 작성하는 방식이 많이 사용되는지도 궁금합니다.
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
프로미스 함수 모킹할 때 질문
Promise.reject를 쓰는 경우가 아니라면 바로 반환하거나 Promise객체를 사용해서 반환하거나 똑같은 것 같은데 크게 상관없나요?
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
레퍼지토리가 여러개인 경우
만약 A라는 인터페이스를 상속받은 B,C,D 클래스가 모두 Repository로 컨테이너에 등록이 되어있고Service에서는 A 인터페이스를 선언하여 컨테이너로부터 주입을 받고자 한다면, 어떤것이 주입되는 것인가요?
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
infinite scroll에서 최초 랜더링한 페이지가 한페이지에 안나올때 추가로 페이지를 불러 올수 있나요?
만약 Page의 단위를 5로 잡아서 최초 랜더링한 페이지가 스크롤이 되지않는다면 onScroll 이벤트가 발생하지 않으니 setSize 이벤트도 발생할수 없습니다.이렇게 최초 데이터의 개수가 모자라서 이벤트 자체가 발생하지 않으면 별개의 이벤트로 scroll이 가능할때까지 페이지를 불러와야 하는데 좋은 방법이 잇는가요?페이지를 5개로 잡을떄페이지를 20개로 잡을떄참조 코드import ChatBox from '@components/ChatBox'; import ChatList from '@components/ChatList'; import useInput from '@hooks/useInput'; import { Header, Container } from '@pages/DirectMessage/styles'; import fetcher from '@utils/fetcher'; import makeSection from '@utils/makeSection'; import axios from 'axios'; import gravatar from 'gravatar'; import React, { FC, FormEventHandler, useCallback, useEffect, useRef, useState } from 'react'; import Scrollbars from 'react-custom-scrollbars-2'; import { useParams } from 'react-router'; import useSWR from 'swr'; import useSWRInfinite from 'swr/infinite'; const PAGE_SIZE = 5; const DirectMessage: FC = () => { const { workspace, id } = useParams(); const { data: myData } = useSWR<IUser, false>('/api/users', fetcher); const { data: userData } = useSWR<IUser, false>(`/api/workspaces/${workspace}/users/${id}`, fetcher); const [chat, onChangeChat, setChat] = useInput(''); // const scrollbarRef = useRef(null); const { data: chatData, mutate: mutateChat, setSize, } = useSWRInfinite<IDM[]>( (index) => `/api/workspaces/${workspace}/dms/${id}/chats?perPage=${PAGE_SIZE}&page=${index + 1}`, fetcher, ); const isEmpty = chatData?.[0]?.length === 0; const isReachingEnd = isEmpty || (chatData && chatData[chatData.length - 1]?.length < PAGE_SIZE); const chatSection = makeSection(chatData ? [...chatData].flat().reverse() : []); const onSubmitForm = useCallback<FormEventHandler>( (event) => { event.preventDefault(); if (!chat || !chat?.trim()) { return; } axios .post(`/api/workspaces/${workspace}/dms/${id}/chats`, { content: chat, }) .then(() => { mutateChat(); setChat(''); }) .catch(console.error); console.log('제출'); }, [chat, id, mutateChat, setChat, workspace], ); const scrollbarRef = useRef<Scrollbars>(null); return !userData || !myData || !chatData ? null : ( <Container> <Header> <img src={gravatar.url(userData.email, { s: '24px', d: 'retro' })} alt={userData.nickname} /> <span>{userData.nickname}</span> </Header> <ChatList chatSections={chatSection} isEmpty={isEmpty} isReachingEnd={isReachingEnd} setSize={setSize} ref={scrollbarRef} /> <ChatBox onSubmitForm={onSubmitForm} chat={chat} onChangeChat={onChangeChat} placeholder={`Message ${userData.nickname}`} otherData={[userData]} /> </Container> ); }; export default DirectMessage;import Chat from '@components/Chat'; import { ChatZone, Section, StickyHeader } from '@components/ChatList/styles'; import React, { FC, MutableRefObject, forwardRef, useCallback } from 'react'; import { Scrollbars, positionValues } from 'react-custom-scrollbars-2'; interface Props { chatSections: { [key: string]: (IDM | IChat)[] }; isEmpty: boolean; isReachingEnd?: boolean; setSize: (f: (size: number) => number) => Promise<(IDM | IChat)[][] | undefined>; } const ChatList = forwardRef<Scrollbars, Props>(({ chatSections, isReachingEnd, isEmpty, setSize }, scrollRef) => { const onScroll = useCallback( (values: positionValues) => { if (values.scrollTop === 0 && !isReachingEnd) { setSize((size) => size + 1).then(() => { const current = (scrollRef as MutableRefObject<Scrollbars>)?.current; if (current) { current.scrollTop(current.getScrollHeight() - values.scrollHeight); } }); } }, [isReachingEnd, scrollRef, setSize], ); return ( <ChatZone> <Scrollbars autoHide ref={scrollRef} onScrollFrame={onScroll}> {Object.entries(chatSections).map(([dateData, chatData]) => ( <Section className={`section-${dateData}`} key={dateData}> <StickyHeader> <button>{dateData}</button> </StickyHeader> {chatData.map((chat) => ( <Chat key={chat.id} data={chat} /> ))} </Section> ))} </Scrollbars> </ChatZone> ); }); export default ChatList;
-
미해결
미라클 모닝 스터디 WAIM 8기 멤버 구해요!
안녕하세요! 성장에 진심인 사람들이 모이는 아침, We are in Morning! WAIM에서 7.31~8.26 기간 진행하는 8기 멤버를 구해요! WAIM은 자신의 라이프 스타일에 맞춰 오전 4시~8시 사이 고정 기상 시간을 정하고'타임스탬프'를 이용해 1차 기상 인증, 2차 루틴 인증을 하는 미라클 모닝 스터디입니다!함께 하며 서로 좋은 영향을 주고 받고 의미있는 아침 시간을 만들어 나가봐요!!📕신청 기간 : 7월 23일~7월 30일 23:59 까지 📕중간 참여 신청 : 7월 31~8월 13일 23:59까지(모집 인원 수 등 상황에 따라 변경될 수 있음에 양해 부탁드립니다.)📆스터디 기간 : 7월 31일~8월 26일, 4주📬오픈카톡방 링크 발송 : 7월 29~30일, 이틀간 (상황에 따라 일정이 변경될 수 있습니다.)프로필 링크에 게재된 홈페이지를 통해 스터디 운영 방식을 꼭! 확인해 보시고 신청하여 주시길 바랍니다 :)홈페이지 및 신청https://waim.oopy.io/인스타그램https://www.instagram.com/weareinmorning/문의 채널http://pf.kakao.com/_zfAWxj
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문
Unable to start assembler. Check your settings.
3.12.1 버전으로 시작했는데도 설정오류가 뜹니다-g -f win64 $SOURCE$ -l $LSTOUTPUT$ -o $PROGRAM.OBJ$로 옵션 설정을 바꿨고 현재 윈도우 10 을 사용중인데 이유를 모르겠네요3.14.0버전도 마찬가지라 버전의 문제가 아닌 것 같습니다.
-
미해결it 취업을 위한 알고리즘 문제풀이 입문 (with C/C++) : 코딩테스트 대비
강의 자료 다운로드가 안됩니다
강의 구매하고 강의자료 다운로드를 받으려는데 먼저 크롬에서 파일 다운로드가 차단돼었고크롬 보안을 끄고 다운로드를 받으려니 바이러스가있어서 다운로드가안된다고 뜨는데왜그런걸까요 ㅠㅠ
-
미해결따라하면서 배우는 고박사의 유니티 기초
총알이 나가지 않습니다.
애러는 안 나는 데, 맨 처음 시작했을 때, 오른쪽으로 나가지 않습니다. 코드는 이렇습니다. public class PlayerControler : MonoBehaviour{ [SerializeField] private KeyCode keyCodeFire = KeyCode.Space; [SerializeField] private GameObject bulletPrefab; private float moveSpeed = 3.0f; private Vector3 lastMoveDirection = Vector3.right; private void Update() { // 플레이어 오브젝트 이동 float x = Input.GetAxisRaw("Horizontal"); float y = Input.GetAxisRaw("Vertical"); transform.position += new Vector3(x, y, 0) moveSpeed Time.deltaTime; // 마지막에 입력된 방향키의 방향을 총알의 발사 방향으로 활용 if (x != 0 || y != 0) { lastMoveDirection = new Vector3(x, y, 0); } // 플레이어 오브젝트 총알 발사 if (Input.GetKeyDown(keyCodeFire)) { GameObject clone = Instantiate(bulletPrefab, transform.position, Quaternion.identity); clone.name = "Bullet"; clone.transform.localScale = Vector3.one * 0.5f; clone.GetComponent<SpriteRenderer>().color = Color.red; clone.GetComponent<Movement2D>().Setup(lastMoveDirection); } }}