묻고 답해요
148만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
@webServlet과 서블릿 컨테이너
안녕하세요. 서블릿의 개념과 역할을 대체로 이해하였으나 기본적인 동작 원리에 대해 궁금점이 생겨 질문 남깁니다. 아래와 같은 흐름으로 이해하였는데, 제가 이해한 흐름이 정확한 것인지 궁금합니다. 1. @webServlet 어노테이션으로 서블릿을 등록해주면, @ServletComponentScan으로 서블릿이 서블릿 컨테이너에 자동 등록 된다. (싱글톤으로 서블릿 객체를 생성하고 관리한다.) 2. HTTP 요청이 들어오면, urlpatterns과 매칭되는 서블릿 객체를 찾아 호출한다. 3. 호출된 서블릿 객체는 request 객체를 개발자가 사용하기 쉽게 변환하고(여기서 또 어떤 메서드가 실행되고) 4. service() 메서드를 호출한다. 여기에 원하는 request, response 값을 조작할 수 있다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
ErrorPage 자동 등록 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 스프링 부트가 ErrorPage를 자동으로 등록해 놓는데, 이때 어떤 예외가 발생하던 기본적으로 /error 라는 경로로 내부호출 되도록 ErrorPage를 등록해 놓는다고 설명해주셨습니다. 그러면 예외 종류 관계없이 예외가 발생하면 내부적으로 /error라는 경로로 재요청 되는데, 그 요청을 BasicErrorController가 받아서, 발생한 예외의 상태코드에 따라서 예외 화면을 보여주는게 맞는지 궁금합니다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
ConverterController.java 에서 제출 버튼 눌렀을 때
안녕하세요 /converter/edit 에서 제출 버튼을 클릭해서 @PostMapping("/converter/edit") public String converterEdit(@ModelAttribute Form form, Model model) { IpPort ipPort = form.getIpPort(); model.addAttribute("ipPort", ipPort); return "converter-view"; } 위 부분이 실행되기 전에 첫줄(IpPort ipPort = form.getIpPort();)에 break 걸고보면 SpringToIpPortConverter : convert source = 127.0.0.1:8080 위 로그가 연속으로 2개 찍힙니다. 하나는 "127.0.0.1:8080" 의 값이 ModelAttribute에 의해 Form 클래스의 IpPort로 컨버전 되어서 찍혔다고 보면 나머지 하나는 왜 찍혔는지 이해가 안 갑니다. 강의 12:38 에도 보면 같은 로그가 마지막에 2개 찍혀 있네요
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
ServletRequest에 대해 궁금한 점이 생겼습니다!
안녕하세요 강의를 듣다 ServletRequest에 대해 궁금한 점이 생겨 질문드립니다! ServletRequest, ServletRequest를 보니 인터페이스 였습니다. 그 후 HttpServletRequest도 보니 ServletRequest를 상속받고 있지만 이 또한 interface 였습니다. 혹시 구현은 어디서 하길래 인터페이스로 선언이 되어 있는 것인가요??
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
message.properties 질문 드립니다.
안녕하세요. 항상 좋은 강의 감사드립니다. 다름아니라 국제화에 대해서 궁금한게 있는데, 제가 m1 맥북에어를 사용중이고 시스템 언어를 영어로 사용중입니다. 그런 상태에서 messages.properties에는 한글로, messages_en.properties로는 영어로 설정되어있습니다. 크롬에서는 언어 순위를 한글로 두었습니다. 이런 상태지만, 애플리케이션 실행하여 확인하면 계속해서 messages_en 파일을 갖고오는 거 같습니다. 아래 테스트 코드에서도 Locale이 한국임에도 에러가 발생합니다..! @Testvoid defaultKR() { assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕"); assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");} Expecting: <"hello"> to be equal to: <"안녕"> but was not. Expected :"안녕" Actual :"hello" 이상하여 한 번 맥북의 시스템 언어를 한글로 바꾸었을때는 한글로 잘 출력은 되었다만, 다시 영어로 바꾸면 크롬의 언어 우선순위와 상관없이 영어가 출력됩니다 ㅠㅠ (개발자 도구에서 accept language가 ko가 1순위인것도 확인했습니다.) 물론 위에서 했던 것처럼 맥북의 시스템 언어를 한글로 바꿔도 되겠지만, 영어인 상태에서도 한글이 정상 출력됨을 보고 싶어서 자문을 구하고 싶습니다. 감사합니다!
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
gradle 프로젝트 open 오류 발생
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용] 처음으로 mac 환경에서 프로젝트를 열려고 하는데 이 오류 때문에 진행을 못하고 있네요.. 프로젝트 파일을 다시 삭제하고 열어봐도 pc를 재부팅하고 다시 시작해도 똑같은 오류가 계속 발생합니다. 구글링에 검색해봐도 마땅한 해결방안이 나오지 않아 답답하네요ㅜㅜ 프로젝트 구성도 이상합니다.. 파일을 열때마다 "._java"와 같은 텍스트 파일이 생겨요
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
bindingFailure 의 용도
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 네3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 네[질문 내용]마지막 부분 10:45 시간에 질문 있습니다. qqqq를 넘기면 컨트롤러에서 타입에러로 자동으로 필드에러를 만들텐데요~ 이후에 어쩔수없이 item.getPrice() 가 null 이니깐 2번째 if 문안에서 addError 메서드로 저희가 선언한 필드에러를 추가하는 것 까지는 이해했습니다. 근데 저희가 선언한 필드에러는 bindingFailure 는 false로, 타입에러?(바인딩실패)는 아니다라고 지정했기 때문에 qqqq와 같은 문자를 넘기면 해당 필드에러는 무시될거라고 생각했습니다 그런데 저희가 추가한 필드에러의 디폴트 메세지가 출력되네요 "가격은 1,000~1,000,000 까지 허용합니다" (bindingFailure가 false이고, 발생한 에러는 타입에러라서 메시지가 표시 안될줄 알았습니다) 그냥 bindingFailure 에 상관없이 필드에러가 추가되었다면 타임리프가 다 처리해버리는 것 인가요 ?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
@CookieValue 타입 컨버팅 관련
안녕하세요. 에러 해결이 어려워 질문을 남기게 되었습니다. 11:40까지 코드를 작성하고 localhost:8080으로 들어가면 Whitelabel Error Page가 뜨는데요. String 타입을 Long타입으로 바꾸는데 실패했다는거 같은데, 9:30에 말씀하신 컨버팅 기능에서 오류가 생긴걸까요? (경고 내용 텍스트: WARN 20100 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "test"]) 이 부분을 지우면 에러가 발생하지 않습니다. @CookieValue(name = "memberId", required = false) 두 컨트롤러의 코드는 다음과 같습니다. LoginController.java package hello.login.web.login;import hello.login.domain.login.LoginService;import hello.login.domain.member.Member;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;import org.springframework.validation.BindingResult;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.PostMapping;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletResponse;@Slf4j@Controller@RequiredArgsConstructorpublic class LoginController { private final LoginService loginService; @GetMapping("/login") public String loginForm(@ModelAttribute("loginForm") LoginForm form) { return "login/loginForm"; } @PostMapping("/login") public String login(@Validated @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) { if (bindingResult.hasErrors()) { return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); if (loginMember == null) { bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다."); return "login/loginForm"; } //로그인 성공 처리 //쿠키에 시간 정보를 주지 않으면 세션 쿠키 (브라우저 종료시 모두 종료) Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getLoginId())); response.addCookie(idCookie); return "redirect:/"; }} HomeController.java package hello.login.web;import hello.login.domain.member.Member;import hello.login.domain.member.MemberRepository;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.CookieValue;import org.springframework.web.bind.annotation.GetMapping;@Slf4j@RequiredArgsConstructor@Controllerpublic class HomeController { private final MemberRepository memberRepository; // @GetMapping("/") public String home() { return "home"; } @GetMapping("/") public String homeLogin(@CookieValue(name = "memberId", required = false) Long memberId, Model model) { if (memberId == null) { return "home"; } //로그인 Member loginMember = memberRepository.findById(memberId); if (loginMember == null) { return "home"; } model.addAttribute("member", loginMember); return "loginHome"; }}
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
th:block 활용
th:block을 현업에서 많이 사용할까요? div두개를 큰 div나 다른 구역을 나누는 html 태그로 나누고 th:each돌리는것보다 렌더링시에 제거되는거 말고 얻을 수 있는 장점이 있을까요?
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
AOP 적용 질문
안녕하세요 AOP 중 궁금한점이 생겨 질문드립니다! [질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 질문을 쭉 봤는데 해당 내용이 없었습니다.3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)네[질문 내용] 이부분에서 찍어보면 다음과 같이 EnhancedBySpring~ 이부분이 스프링이 AOP 를 처리하기 위해 프록시로 복제된 객체로, AOP를 사용하게되면 사용된다는 내용으로 이해했습니다. 강의 자료에 보면, 다음과 같은 내용이 있는데, AOP와 관련된 코드들 (예제에서는 시간을 측정하는 코드, 시간측정하는 클래스를 주입하는 코드)을 제거 한 후에도 MemberController 에서 Service 클래스를 sout 으로 찍어볼 경우, 똑같이 EnhancedBySpring~ 하는 객체가 찍혀 나옵니다. 이 부분이 위에 AOP 적용 전 그림 에서 진짜 객체들만 사용한다는 위 그림과 과 맞지 않는데, 그렇다면 Spring 에서는 AOP 관련된 코드가 없어도 자동적으로 AOP를 위해 프록시 객체를 사용하도록 강제(?) 처리 되는건지 궁금합니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
webapp index.html 관련
webapp 폴더 추가 후index.html을 생성 후 서버를 기동시키고 접속하였을 때 Whitelabel Error Page가 발생 할 경우 입니다. Intellij에서 빈 모듈(spring-mvc) 생성 후 빈 모듈 안에서 새로운 모듈(servlet)을 생성하였을 경우 index.html에 접속이 되지 않는 것 같습니다. 실행 후 Tomcat의 로그에서도 WelcomePage 관련 로그가 출력되지 않았으며 http://localhost:8080과 http:localhost:8080/index.html에 접속할 수 없었습니다. 이후 프로젝트를 종료하고 빈 모듈(spring-mvc)이 아닌 빈 모듈 안에서 생성한 새로운 모듈(servlet)의 build.gradle을 통해 프로젝트를 새로 연 후 서버를 기동하면 WelcomePage 관련 로그가 출력 되며 http://localhost:8080에 문제없이 index.html에 접속할 수 있었습니다. 원인은 아직 잘 모르겠지만 멀티 모듈 또는 빈 모듈에서 새로운 모듈을 생성한 경우 Intellij에서 webapp을 찾지 못하는것 같습니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
app-plain.jar의 역할
안녕하세요, 수강 중에 궁금한 점이 있어 질문드립니다. :) 스프링 부트 프로젝트를 ./gradlew build 명령어로 빌드를 하고 나면, hello-plain.jar와 hello.jar의 두 개의 jar 파일이 생성됩니다. 구글링을 해 본 결과, 스프링 부트에서 빌드하면 Plain Archive도 함께 만들어지며 이를 방지하고 싶으면 build.gradle에 별도의 설정을 하면 되는 것, 그리고 Plain Archive가 무엇인지도 알게 되었습니다. 그런데 Plain Archive는 어디에 쓰려고 만들어진 건지, 목적이 무엇인지를 모르겠습니다. 의존성이 포함되지 않은 파일이 왜 필요한 건가요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
왜 UUID로 find하지 않고 SESSION_COOKIE_NAME으로 find 하나요?
왜 UUID로 find하지 않고 SESSION_COOKIE_NAME으로 find 하나요? 그렇게하면 보안상 사용한다던 UUID가 의미가 없지않나요? 해킹하는 사람 입장에서는 쿠키 이름만 알아도 정보를 가져올수있으니까요
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
{itemId} 매핑원리 질문입니다.
@PostMapping("/{itemId}/edit")public String edit(@PathVariable long itemId,@ModelAttribute Item updateParam) { itemRepository.update(itemId,updateParam); return "redirect:/basic/items/{itemId}"; } 위 코드의 메서드가 실행될 때, {itemId} 에 @PathVariable long itemId 가 매핑되는데 매핑되는 원리가 궁금합니다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
세션 형태 관련 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 직접 만든 세션에서는 세션id로 UUID를 이용해서 랜덤한 값을 만들어서 세션 보관을 하고 이 세션id를 쿠키의 값으로 전달했는데 HttpSession의 경우는 쿠키가 다음과 같다고 하셨습니다. JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05 그럼 5B78E23B513F50164D6FDD8C97B0AD05 가 톰캣이 만든 세션id인가요? 다른 글에서 세션들을 보관하는 세션 저장소가 하나 더 있다고 봤는데 위에서 랜덤값인 세션 id가 key 이고 value가 Map(편의상 Map2) 인 Map형태의 세션 저장소가 있고 value인 Map2 또한 세션 저장소로 랜덤한 세션id 로 인증된 특정한 사용자만 사용하는건가요? Map2에서 저희가 session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember); 등록한게 있는거 같구요. 만약 제가 생각한게 맞다면 굳이 왜 이런 형태를 이용하나요? 직접 만든 Session 처럼 처음부터 랜덤한 값을 세션 키로 이용하면 Map 하나로 해결될것 같아서요.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
itemRepository.save(item); 부분에서 궁금한 것이 있습니다 !
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 안녕하세요 영한님, 서포터즈님! 강의 너무너무 잘 보고있습니다. 다름이 아니라 itemRepository.save(item) 부분에서 궁금한게, 영한님은 따로 Item savedItem 이라는 변수를 만들어서 redirectAttributes.addAttribute("itemId", savedItem.getId()) 이렇게 사용하시는데 저는 굳이 변수를 따로 만드는게 아니라, @ModelAttribute Item item 객체를 가지고 redirectAttributes.addAttribute("itemId", item.getId()); 라고 상용해도 되지 않을까? 라는 궁금증이 생겼습니다. 실행은 문제가 없었는데, 문제될 점이 따로 있을까요 ?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
update 로직 짤때
위 사진처럼 짜면 put으로 있던 id에 updateParam으로 덮어씌워져서 업데이트 되는거 아닌가요? 테스트도 정상적으로 작동하는데 위에 4줄로 작성하면 더 좋은점이 어떤게 있나요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
테스트에서의 bindingResult 오작동
안녕하세요. 스프링 Junit 테스트로 검증 부분을 테스트 하던 중 의도대로 되지 않는 부분이 있어서 질문드립니다. 먼저 테스트 하려는 컨트롤러는 JSON 데이터를 파라미터로 받아서 검증을 하고 (@Valid @RequestBody) 검증이 실패하면(bindingResult.hasErrors가 true일 때) 예외를 발생시키는데, 해당 예외는 컨트롤러 어드바이스로 받아 http 400코드를 응답 하게끔 설계를 하였습니다. (포스트맨으로 검증에 실패하는 데이터를 보냈을 때 400으로 응답되는 것을 확인하였습니다.) 그런데 테스트 코드에선 검증에 통과못하는 데이터를 넣어 mockMvc.perform으로 테스트 해보니 기대와는 다르게 http 200코드가 찍히면서 검증이 통과되는 결과가 나왔습니다. (andExpect에서 getResolvedException()이 null로 찍히는 것을 보니 bindingResult.hasErrors가 false가 되는 것 같습니다) 컨트롤러 어드바이스가 문제인가 싶어서 다른 예외를 발생시켜 테스트 해보았는데, 다른 예외는 잘 처리되더라구요. 혹시 이러한 검증 로직을 mockMvc로 테스트 하는 방법이 따로 있는 것 일까요? 스프링은 5.x.x 버전이며 junit은 4.12 버전을 사용 중 입니다.
-
미해결[리액트 1부] 만들고 비교하며 학습하는 리액트 (React)
Tab 상수 관련되서 질문 드립니다.
TabLable의 프로퍼티명을 상수 객체로 선언하신 이유가 있을까요? 추후 구현에서 TabType에 따라 안의 컨텐츠가 달라지니 map안에서 type === 'KEYWORD'와 같이 하드코딩 방지하기 위해서 일까요? (1) const TabType = { KEYWORD: 'KEYWORD', // 추천 HISTORY: 'HISTORY' // 최근 }; const TabLable = { [TabType.KEYWORD]: '추천 검색어', [TabType.HISTORY]: '최근 검색어' } Object.values(TabType) .map(tabType => ({ tabType, tabLable: TabLable[tabType] })) .map(this._getTab) .join('') ------------------------------- (2) const TabLable = { 'KEYWORD': '추천 검색어', 'HISTORY': '최근 검색어' } Object.entries(TabLable) .map((row) => { const [ tabType, tabLable ] = row; return { tabType, tabLable } }) .map(this._getTab) .join('');
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
요즘은 장기 로그인 유지를 많이 하는 거 같은데 맞나요?
원칙적으론 보안에 위협이 되겠지만 배민이나 쿠팡이나 등등 쇼핑 서비스를 보면 로그아웃을 하지 않는 이상 로그인이 유지시키는 것 같은데 아무래도 편의성 때문에 그렇겠죠? 그런 큰 서비스들은 회원 수만 몇천만일텐데 이런 경우도 다 메모리에 저장하고 있는 것인가요? 위와 같은 초대형의 사례들에선 세션 유지관리의 개념과 철학이 아예 근본부터 다를 것 같은데 어떤 식으로 관리 되는 것인지 궁금합니다. 아니면 그런 거 없이 동일한 건가요?