반응형 Framework & Library176 [Spring Boot] : 서블릿 HTTP 세션을 활용해 로그인 처리하기(2) @SessionAttribute를 통해 스프링에서 세션을 조금 더 편리하게 사용할 수도 있다. 이미 로그인 된 사용자를 찾을 때는 다음과 같이 사용하면 된다. @SessionAttribute = "loginMember", required=false) Member loginMember 이런 형식으로 사용하면 되는데 강의에서 활용한 것은 아래와 같다. public String homeLoginV3Spring( @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) { 전에 만들어둔 상수를 넣어준다. 세션에 회원 데이터가 없으면 home if (loginMember == null) .. 2022. 2. 22. [Spring Boot] : 서블릿 HTTP 세션을 활용해 로그인 처리하기(1) 세션은 대부분 웹 애플리케이션에 필요한 개념이다. 서블릿은 세션을 위해 HttpSession이라는 기능을 제공하고 우리가 직접 구현한 것 보다 더 잘 구현되어 있다. | HttpSession 소개 서블릿이 제공하는 HttpSession도 강의에서 만들어 본 SessionManager와 같은 방식으로 동작한다. 서블릿을 통해 HttpSession을 생성하면 쿠키의 이름이 JSESSIONID이고 값은 UUID와 같은 값을 생성하게 된다. public static final String LOGIN_MEMBER = "loginMember"; 데이터를 보관하고 조회할 때 중복 사용되므로 상수 하나를 정의했다. //세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성 HttpSession session = req.. 2022. 2. 22. [Spring Boot] : 직접 만든 세션 적용하여 로그인 처리하기 private final SessionManager sessionManager; 우선 전에 만든 sessionManager를 주입 받는다. @PostMapping("/login") public String loginV2(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) { if (bindingResult.hasErrors()) { return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); if (loginMember == null) { binding.. 2022. 2. 21. [Spring Boot] : Session 직접 만들기 로그인 처리하기 - 세션 직접 만들기 우선, Session ID를 만들어야 하는데 추정이 불가능하게 만들어 줘야 한다. 여기에 UUID를 사용한다. 클라이언트와 서버는 쿠키로 연결이 되어야 한다. 이번에는 세션을 직접 개발해서 적용을 해봤다. 세션 관리는 3가지 기능으로 제공되는데 세션 생성과 세션 조회, 세션 만료로 나누어진다. @Component public class SessionManager { public static final String SESSION_COOKIE_NAME = "mySessionId"; private Map sessionStore = new ConcurrentHashMap(); @Component로 스프링 빈으로 자동 등록을 시켜주고 ConcurrentHashMap을 사용했다.. 2022. 2. 18. [Spring Boot] : Cookie 보안 문제와 대처 방안 Cookie 보안 문제 쿠키 값은 바꿔치기가 가능하기 때문에 그냥 개발하면 사용자가 다 털려 버리게 된다. 즉, 쿠키의 값을 임의로 변경할 수 있다. 쿠키의 값을 임의로 변경하게 되면 다른 사용자도 유추해 낼 수 있고 쿠키에 관한 정보도 훔쳐갈 수 있게 된다. 만약 쿠키에 중요한 카드 정보가 등록되어 있다면 모두 다 털려버리는 것이다. 그리고 쿠키는 해커가 한번 훔쳐가면 평생 사용할 수 있다고 한다. 그것으로 악의적인 요청을 지속적으로 시도할 수 있다. 대안으로는 쿠키에 중요한 값을 노출하지 않고 랜덤 값을 노출한다. 그리고 서버에서 토큰과 사용자 id를 매핑해서 인식하도록 하고 서버에서 토큰을 관리하면 된다. 또, 토큰을 털어가도 시간이 지나면 사용할 수 없도록 토큰의 만료 시간을 짧게(예시는 30분으.. 2022. 2. 18. [Spring Boot] : Cookie와 로그아웃 구현하기 Cookie와 로그아웃 구현 먼저 로그인 상태를 유지하기 위해 쿠키를 사용할 수 있다. 서버에 로그인하면 HTTP 응답에 쿠키를 담아 브라우저에 전달해주면 브라우저는 앞으로 해당 쿠키를 지속해서 보내주게 된다. 시작하기 전에 쿠키의 종류에 대해서도 알려줬다. 영속 쿠키와 세션 쿠키가 있다. 영속 쿠키는 만료 날짜를 입력하면 해당 날짜까지 유지되고 세션 쿠키는 만료 날짜를 생략하면 브라우저 종료시 까지만 유지가 된다. 브라우저 종료시 로그아웃이 되는 것을 바란다면 세션 쿠키를 사용하면 된다. Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId())); response.addCookie(idCookie); return "redi.. 2022. 2. 18. [Spring Boot] : 로그인 기능 구현하기 이번에는 로그인 기능을 개발해 봤다. 로그인의 핵심 기능은 회원을 조회하고 파라미터로 넘어온 password와 비교해 같으면 회원을 반환하고 다르면 null을 반환하는 것이 로그인의 핵심 비즈니스 로직이라고 한다. Optional findMemberOptional = memberRepository.findByLoginId(loginId); Member member = findMemberOptional.get(); if (member.getPassword().equals(password)) { return member; } else { return null; } Optional을 통해 이렇게 서비스 로직을 작성했는데 강사님이 비추하고 람다식으로 코드를 작성하는 것을 추천하셨다. 그렇게 작성한 코드가 아래와.. 2022. 2. 17. [Spring Boot] : 회원 가입 기능 구현하기 회원가입 기능 구현하기 @Data public class Member { private Long id; @NotEmpty private String loginId; //로그인 ID @NotEmpty private String name; @NotEmpty private String password; } 회원가입을 위한 객체 클래스를 만들어준다. @Slf4j @Repository public class MemberRepository { private static Map store = new HashMap(); //static 사용 private static long sequence = 0L; //static 사용 public Member save(Member member) { member.setId(++se.. 2022. 2. 17. [Spring Boot] : Bean Validation HTTP message Converter @Valid와 @Validated는 HttpMessageConverter(@RequestBody)에도 적용할 수 있다. @ModelAttrubute는 HTTP 요청 파라미터(URP 쿼리 스트링, POST Form)를 다룰 때 사용하고 @RequestBody는 HTTP Body의 데이터를 객체로 변환할 때 사용한다. @Slf4j @RestController @RequestMapping("/validation/api/items") public class ValidationItemApiController { @PostMapping("/add") public Object addItem(@RequestBody @Validated ItemSaveForm form, BindingResult bindingResult).. 2022. 2. 17. [Spring Boot] : Form 전송 객체 분리하여 개발하기 등록할 때 사용하는 검증과 수정할 때 사용하는 검증을 분리시켜 구현했다. 전에는 groups를 통해 분리를 해서 사용했지만 그렇게 사용할 경우 코드가 복잡해지기도 하고 내용이 많아지면 유지보수가 어려워진다는 문제가 있어서 이번에는 아예 분리를 했다. 현업에서도 이렇게 많이 사용을 한다고 한다. @Data public class ItemSaveForm { @NotNull @Max(value=9999) private Integer quantity; } 먼저 ItemSaveForm라는 DTO(데이터 전송 객체)를 만들어 준다. 등록에서 사용한다. @Data public class ItemUpdateForm { @NotNull private Long id; // 수정에서는 수량은 자유롭게 변경할 수 있다. pr.. 2022. 2. 17. [Spring Boot] : Bean Validation 한계와 해결하기 위한 groups Bean Validation 한계와 해결하기 위한 groups 만약 변경 사항이 들어와서 @Max(value = 9999) //수정 요구사항 추가 private Integer quantity; 수정 할 때에는 한계값이 없어야 한다. 간단하게 여기서 어노테이션을 수정할 수 있겠지 싶지만 사실 그렇게 되면 등록을 할 때에도 한계값이 사라지기 때문에 요구사항에 맞지 않게 된다. 어노테이션을 분리해서 적용해야 하는데 그렇게 하기 위한 기능이 groups 기능이라고 보면 된다. 먼저 인터페이스 두 개를 만들어주었다. public interface SaveCheck { } public interface UpdateCheck { } 인터페이스 자체에 코드가 들어가진 않는다. @Max(value = 9999, grou.. 2022. 2. 16. [Spring Boot] : Bean Validation 수정에 적용하기 Bean Validation 수정에 적용하기 상품 수정에도 검증을 적용했다. public String edit(@PathVariable Long itemId, @Validated @ModelAttribute Item item, BindingResult bindingResult) Item 모델 객체에 @Validated를 추가해 줬다. //특정 필드 예외가 아닌 전체 예외 if (item.getPrice() != null && item.getQuantity() != null) { int resultPrice = item.getPrice() * item.getQuantity(); if (resultPrice < 10000) { bindingResult.reject("totalPriceMin", new Obj.. 2022. 2. 16. [Spring Boot] : Bean Validation Obejct 오류 특정 필드가 아닌 오브젝트 관련 오류는 @ScriptAssert()를 활용하면 된다. @ScriptAssert(lang = "javascript", script = "_this.price * _this.quantity >= 10000", message = "총 합이 만원이 넘게 해주세요.") 이렇게 어노테이션을 활용해서 써주면 되는데 기능이 너무 약해서 그냥 이 부분은 Java 코드로 가져오는 게 낫다고 한다. //특정 필드가 아닌 복합 룰 검증 if (item.getPrice() != null && item.getQuantity() != null) { int resultPrice = item.getPrice() * item.getQuantity(); if(resultPrice < 10000) { bin.. 2022. 2. 16. [Spring Boot] : Bean Validation 에러 코드 Bean Validation 에러 코드 Bean Validation이 제공하는 오류 메시지를 좀 더 자세히 변경하고 싶으면 Bean Validation을 적용하고 bindingResult에 등록된 검증 오류 코드를 본다. 오류 코드가 어노테이션 이름으로 등록이 되는데 typeMismatch와 유사하다. NotBlank라는 오류 코드를 기반으로 MessageCodesResolver를 통해 다양한 메시지 코드가 순서대로 생성된다. NotBlank={0} 공백X 이렇게 적었을 때 공백 x 라는 메시지가 뜨지만 조금 더 자세히 하고 싶을 경우 NotBlank.item.itemName=상품 이름을 적어주세요. 이렇게 적어주면 상품 이름을 적어주세요.가 나오게 된다. Bean Validation이 메시지를 찾는 순.. 2022. 2. 16. [Spring Boot] : Bean Validation 소개 및 스프링 적용하기 Bean Validation 소개 및 스프링 적용하기 검증 기는을 위해 매번 코드를 작성하지 않아도 어노테이션을 활용하여 검증을 할 수 있었다. @NotBlank private String itemName; @NotNull @Range(min = 1000, max = 1000000) private Integer price; 이렇게 어노테이션을 활용해서 검증 로직을 사용할 수 있게 공통화, 표준화 한 것이 Bean Validation이다. Bean Validation은 구현체가 아닌 검증 어노테이션과 여러 인터페이스의 모음이다. Bean Validation을 구현한 기술 중 일반적으로 사용하는 구현체가 하이버네이트 Validator이다.(이름이 하이버네이트지만 ORM과 상관이 없다.) Bean Valida.. 2022. 2. 14. [Spring Boot] : Validator 분리(2) Validator 분리 2 스프링이 Validator 인터페이스를 별도로 제공하는 이유가 체계적으로 검증 기능을 도입하기 위해서라고 한다. 이번에는 Validator인터페이스를 사용해 검증기를 만들어 스프링의 추가적인 도움을 받아 적용해 보는 강의를 들었다. @InitBinder //항상 호출이 되다. 여기에서 검증기를 넣는다. 이 컨트롤에서만 실행이 된다. public void init(WebDataBinder dataBinder) { dataBinder.addValidators(itemValidator); } @InitBinder를 만들어 주었다. InitBinder는 컨트롤러가 호출이 될 때마다 항상 호출이 된다. 여기에 검증기를 넣었다 지금은 이 컨트롤러에서만 실행이 되는데 @SpringBootA.. 2022. 2. 11. [Spring Boot] : Validator 분리(1) Validator 분리 1 길고 복잡한 검증 로직을 컨트롤러에서 떼어내는 작업을 했다. 어떻게 떼어냈냐면 따로 클래스를 만들고 여기에 검증 로직을 넣었다. 그리고 컨트롤러에서 불러서 사용하기만 하면 되는 구조로 변경하였다. @Component public class ItemValidator implements Validator { @Override public boolean supports(Class clazz) { return Item.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { Item item = (Item) target; 검증 로직 들어가는 부분 } 이렇게 코드를 짜주었다.. 2022. 2. 11. [Spring Boot] : 검증 오류 코드 2가지 확인하기 검증 오류 코드는 2가지로 나눌 수 있는데 개발자가 직접 설정한 오류 코드와 스프링이 직접 검증 오류에 추가한 경우 이 두가지가 있다. 예를 들면 가격은 Integer로 설정이 되어 있는데 String 값이 들어가면 위와 같은 오류가 난다. typeMismatch.java.lang.Integer=숫자를 입력해주세요. typeMismatch=타입 오류입니다. 로그에 출력된 타입 오류 코드를 errors.properties에 추가하고 다시 돌려본다. 스프링이 만든 오류 메시지가 뜨지 않고 위에서 설정한 오류 메시지가 나오게 된다. 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 참고하여 공부하였습니다. 2022. 2. 11. [Spring Boot] : 구체적에서 덜 구체적으로 오류 코드 메시지 처리하기 강의에서 핵심은 구체적 → 덜 구체적으로 만들어 주는 것이 핵심이라고 했다. 그것을 강의에서 레벨로 표현하여 알려주었는데 아래와 같다. #==FieldError== #Level1 required.item.itemName=상품 이름은 필수입니다. #Level2 - 생략 #Level3 타입을 알 수 있다. required.java.lang.String = 필수 문자입니다. #Level4 required = 필수 값 입니다. 레벨 1이 먼저 매칭되고 1이 없다면 그 다음 2가 매칭이 된다. 그 다음이 3, 4순으로 매칭이 되게 된다. 만드는 순서는 4부터 만들지만 매칭되는 순서는 1부터 매칭이 되는 것이다. 검증 오류 메시지가 발생하면 레벨 1순으로 MessageSource에서 찾게되는 것이다. 이렇게 되면 .. 2022. 2. 11. [Spring Boot] : MessageCodesResolver를 통한 검증 오류 코드로 메시지 코드 생성 MessageCodesResolver를 사용해 검증 오류 코드로 메시지 코드를 생성했다. MessageCodesResolver는 인터페이스이고 DefaultMessageCodesResolver는 기본 구현체이며 주로 ObjectError, FieldError와 같이 사용한다고 한다. rejectValue()와 reject()는 내부에서 MessageCodesResolver를 사용하며 여기서 메시지 코드를 생성한다고 한다. FieldError와 ObjectError의 생성자는 여러개의 오류 코드를 가질 수 있고 MessageCodesResolver를 통해 생성된 순서대로 오류 코드를 보관한다. 오류 메시지가 어떻게 출력되나 정리하자면 타임리프 화면을 렌더링할 때 th:errors가 실행되게 된다. 이때 오.. 2022. 2. 11. [Spring Boot] : 어떻게 오류 코드를 설계 할 것인가? 어떻게 오류 코드를 설계 할 것인가 required.item.itemName=상품 이름은 필수입니다. range.item.price=가격은 {0} ~ {1} 까지 허용합니다. max.item.quantity=수량은 최대 {0} 까지 허용합니다. totalPriceMin=가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1} 오류 코드를 이제까지 이런 식으로 작성을 했는데 이렇게 작성하는 것의 단점은 범용성이 떨어진다는 것이다. 그것을 해결하기 위해 조금 더 단순하게 만들 수도 있다. required=필수 값 입니다. range=범위는 {0} ~ {1} 까지 허용합니다. max=최대 {0} 까지 허용합니다. 바로 이렇게 조금 더 간단하게 만들 수 있는데 또 역설적이게도 단점은 디테일이 떨어진.. 2022. 2. 11. 이전 1 2 3 4 5 6 ··· 9 다음 반응형