Home

0

Spring boot - 직접 Session Storage 구현하기

목차 Spring boot - HttpSession 사용하기 Spring boot - 직접 Session Storage 구현하기 Spring boot - Cookie 사용하기 직접 만든 Session 사용하기@Componentpublic class SessionManager { public static final String SESSION_COOKIE_NAME = "mySessionId"; private Map<String, Object> sessionStore = new ConcurrentHashMap<>(); public void createSession(Object value, HttpServletResponse response){ // Session id를 상성하고 값을 Session에 저장 String sessionId = UUID.randomUUID().toString(); // Session Store 에 새로운 Session 정보를 저장한다. // Session 은 Key Value 형태로 데이터를 저장 sessionStore.put(sessionId, value); // 쿠키 생성 // Session Id 를 Cookie 에 넣어 전달한다. Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId); response.addCookie(mySessionCookie); } public Object getSession(HttpServletRequest request){ // Request 에서 Session Id 를 가져온다. Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME); if(sessionCookie == null){ return null; } // Session Store 에 저장된 Session 정보를 가져온다. return sessionStore.get(sessionCookie.getValue()); } public void expire(HttpServletRequest request){ Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME); if(sessionCookie != null){ sessionStore.remove(sessionCookie.getValue()); } } public Cookie findCookie(HttpServletRequest request, String cookieName){ if(request.getCookies() == null){ return null; } return Arrays.stream(request.getCookies()) .filter(cookie -> cookie.getName().equals(cookieName)) .findAny() .orElse(null); }} @PostMapping("/login")public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response){ log.info("Request Params : {}", form); if(bindingResult.hasErrors()){ return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); if(loginMember == null){ bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다."); return "login/loginForm"; } // 로그인 설공 처리 TODO // 쿠키 사용하기 // 쿠키에 시간 정보를 주지 않으면 세선 쿠(브라우저 종료시 모두 종료) Cookie cookie = new Cookie("memberId", String.valueOf(loginMember.getId())); response.addCookie(cookie); return "redirect:/";} Testclass SessionManagerTest { SessionManager sessionManager = new SessionManager(); @Test void SessionTest(){ // 세션 생성 MockHttpServletResponse response = new MockHttpServletResponse(); Member member = new Member(); sessionManager.createSession(member, response); // 요청에 응답 쿠키 저장 MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(response.getCookies()); // 세션 조회 Object result = sessionManager.getSession(request); assertThat(result).isEqualTo(member); // 세션 만료 sessionManager.expire(request); Object expired = sessionManager.getSession(request); assertThat(expired).isNull(); }} @PostMapping("/login")public String loginV2(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response){ log.info("Request Params : {}", form); if(bindingResult.hasErrors()){ return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); if(loginMember == null){ bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다."); return "login/loginForm"; } // 로그인 설공 처리 TODO // 세션 관리자를 통해 세션을 생성하고, 회원 데이터 보관 // 쿠키에 시간 정보를 주지 않으면 세션 쿠키(브라주어 종료시 모두 종료) sessionManager.createSession(loginMember, response); return "redirect:/";} @PostMapping("/logout")public String logoutV2(HttpServletRequest request){ sessionManager.expire(request); return "redirect:/";}

0

Spring boot - Cookie 사용하기

목차 Spring boot - HttpSession 사용하기 Spring boot - 직접 Session Storage 구현하기 Spring boot - Cookie 사용하기 쿠키 생성스프링에서 쿠키는 Cookie 를 이용해 쉽게 생성할 수 있습니다. 생성한 쿠키를 Client 로 전달하기 위해 컨트롤러 메소드 파라미터로 HttpServletResponse 객체를 추가한 후 HttpServletResponse 객체에 생성한 쿠키를 넣어줍니다. @GetMapping("/cookie/create")public ResponseEntity<String> helloCookie(HttpServletResponse httpServletResponse) { Cookie cookie = new Cookie("helloCookie", URLEncoder.encode("Hello world", StandardCharsets.UTF_8)); httpServletResponse.addCookie(cookie); return ResponseEntity.ok().body("Create Cookie");} 쿠키 생성 테스트@Testvoid CookieTest() throws Exception { ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/cookie/create")) .andExpect(status().isOk()) .andExpect(cookie().exists("helloCookie")) .andExpect(cookie().value("helloCookie", "Hello+world")) .andDo(print());} TestRestTemplate 을 이용해 어플리케이션에 대한 컨트롤러 테스트를 진행합니다. Client 로 전달된 쿠키는 Response Header 에서 확인할 수 있습니다. @LocalServerPortprivate int port;@Autowiredprivate TestRestTemplate restTemplate;@Testvoid helloCookie() { String url = "http://localhost:" + port + "/cookie/create"; // 테스트할 엔드포인트 URL // REST API 호출 ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); // 응답 헤더에서 쿠키 정보 가져오기 HttpHeaders headers = response.getHeaders(); List<String> cookies = headers.get(HttpHeaders.SET_COOKIE); assertThat(response.getBody()).isEqualTo("Create Cookie"); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); for (String cookie : cookies) { System.out.println("cookie = " + cookie); assertThat(cookie.contains("helloCookie")).isTrue(); }}

0

백준 14592 - 연구소

백준 14592 - 연구소https://www.acmicpc.net/problem/14592 문제풀이해당 문제에서는 3가지 입력 N, M, 지도 모양이 주어진다. N : 세로 크기 M : 가로 크기 지도 정보 지도 내 상태는 3가지 상태가 존재한다. 0 : 빈칸 1 : 벽 2 : 바이러스 알고리즘 실행계획은 다음과 같이 진행한다. 벽을 세운다. 바이러스 확산 시킨다. 바이러스 영역을 확인한다.

0

백준 1158 - 요세푸스 문제

백준 1158 - 요세푸스 문제https://www.acmicpc.net/problem/1158 문제 풀이해당 문제에서는 두가지 입력 N, K가 주어진다. N : 사람 수 K : 원에서 사람이 제거되는 간격 N, K의 최대 5000 이므로, 최대 O(N^2)의 시간복잡도내에서 문제를 해결해야 한다. 전체 소스 코드import java.io.*;import java.util.*;public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] arguments = br.readLine().split(" "); int N, K; N = Integer.parseInt(arguments[0]); K = Integer.parseInt(arguments[1]); List<Integer> list = new ArrayList<>(); for (int i = 0; i < N; i++) { list.add(i + 1); } int index = 0; StringBuilder sb = new StringBuilder(); sb.append("<"); // 사람을 간격에 맞게 하나씩 제거해준다. while (list.size() > 1) { index += K - 1; index %= list.size(); int removedValue = list.remove(index); sb.append(Integer.toString(removedValue)); sb.append(", "); } // 마지막 숫자를 제거한다. index += K - 1; index %= list.size(); int removedValue = list.remove(index); sb.append(Integer.toString(removedValue)); sb.append(">"); System.out.println(sb.toString()); br.close(); }}

0

Spring boot - Resource 추상화

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 Spring boot Resource 추상화https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/resources.html Resource 는 java.net.URL 을 추상화 해 스프링 프레임워크에서 Low-Level에 있는 자원에 쉽게 접근할 수 있도록 지원한다 ClassPath를 기준으로 리소스를 가져오는 기능의 부재 ServletContext를 기준으로 상대 경로를 읽어오는 기능의 부재 Resource.java public interface Resource extends InputStreamSource { boolean exists(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; @Nullable String getFilename(); String getDescription(); default boolean isReadable() { return exists(); } default boolean isOpen() { return false; } default boolean isFile() { return false; } default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); }} Resource 구현체

0

Spring boot - 파일 다운로드 서비스 구현하기

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 파일 다운로드서버에 저장된 파일을 다운 받기 위해서는 Response 해더에 Content-Disposition 정보가 필요합니다. String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\""; Resource 객체로 업로드된 파일을 전송한다. UrlResource 객체를 이용해 특정 경로에 저장된 Resource 를 가져온 후 ResponseEntity 의 Body에 넣어서 반환한다. @GetMapping("/attach/{itemId}")public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException { Item item = itemRepository.findById(itemId); String storeFileName = item.getAttachFile().getStoreFileName(); String uploadFileName = item.getAttachFile().getUploadFileName(); UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName)); log.info("uploadFileName={}", uploadFileName); String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8); String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\""; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) .body(resource);} 전체 코드@Slf4j@Controller@RequiredArgsConstructorpublic class ItemController { private final ItemRepository itemRepository; private final FileStore fileStore; @GetMapping("/items/new") public String newItem(@ModelAttribute ItemForm form) { return "item-form"; } @PostMapping("/items/new") public String saveItem(@ModelAttribute ItemForm form, RedirectAttributes redirectAttributes) throws IOException { UploadFile attachFile = fileStore.storeFile(form.getAttachFile()); List<UploadFile> storeImageFiles = fileStore.storeFiles(form.getImageFiles()); //데이터베이스에 저장 Item item = new Item(); item.setItemName(form.getItemName()); item.setAttachFile(attachFile); item.setImageFiles(storeImageFiles); itemRepository.save(item); redirectAttributes.addAttribute("itemId", item.getId()); return "redirect:/items/{itemId}"; } @GetMapping("/items/{id}") public String items(@PathVariable Long id, Model model) { Item item = itemRepository.findById(id); model.addAttribute("item", item); return "item-view"; } @ResponseBody @GetMapping("/images/{filename}") public Resource downloadImage(@PathVariable String filename) throws MalformedURLException { return new UrlResource("file:" + fileStore.getFullPath(filename)); } @GetMapping("/attach/{itemId}") public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException { Item item = itemRepository.findById(itemId); String storeFileName = item.getAttachFile().getStoreFileName(); String uploadFileName = item.getAttachFile().getUploadFileName(); UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName)); log.info("uploadFileName={}", uploadFileName); String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8); String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\""; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) .body(resource); }}

0

Spring Cloud - 33. Order Kafka Producer

목차 Spring Cloud - 33. Order Kafka Producer Spring Cloud - 32. kafka-mariadb 연결 Spring Cloud - 31. Kafka Topic 적용 Order Kafka Producer@RestController@RequestMapping("/order-service/")@RequiredArgsConstructorpublic class OrderController { private final OrderService orderService; private final KafkaProducer kafkaProducer; private final OrderProducer orderProducer; @PostMapping(value = "/{userId}/orders") public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder orderDetails){ ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); OrderDto orderDto = modelMapper.map(orderDetails, OrderDto.class); orderDto.setUserId(userId); orderDto.setOrderId(UUID.randomUUID().toString()); orderDto.setTotalPrice(orderDetails.getQty() * orderDetails.getUnitPrice()); ResponseOrder returnValue = modelMapper.map(orderDto, ResponseOrder.class); kafkaProducer.send("example-order-topic", orderDto); orderProducer.send("orders", orderDto); return ResponseEntity.status(HttpStatus.CREATED).body(returnValue); } @GetMapping(value = "/{userId}/orders") public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId){ Iterable<OrderEntity> orderList = orderService.getOrdersByUserId(userId); List<ResponseOrder> result = new ArrayList<>(); orderList.forEach(v -> { result.add(new ModelMapper().map(v, ResponseOrder.class)); }); return ResponseEntity.status(HttpStatus.OK).body(result); }} @Service@Slf4j@RequiredArgsConstructorpublic class OrderProducer { private final KafkaTemplate<String, String> kafkaTemplate; public OrderDto send(String topic, OrderDto orderDto){ ObjectMapper mapper = new ObjectMapper(); String jsonInString = ""; try{ jsonInString = mapper.writeValueAsString(orderDto); }catch (JsonProcessingException ex){ ex.printStackTrace(); } kafkaTemplate.send(topic, jsonInString); log.info("Kafka Producer sent data from the Order microservice: " + orderDto); return orderDto; }} @Data@Builderpublic class Scheme { private String type; private List<Field> fields; private boolean optional; private String name;} @Data@AllArgsConstructorpublic class Field { private String type; private boolean optional; private String field;} @Data@Builderpublic class Payload { private String order_id; private String user_id; private String product_id; private int qty; private int unit_price; private int total_price;} @Data@AllArgsConstructorpublic class KafkaOrderDto implements Serializable { private Scheme scheme; private Payload payload;}

0

Spring Cloud - 32. kafka-mariadb 연결

목차 Spring Cloud - 33. Order Kafka Producer Spring Cloud - 32. kafka-mariadb 연결 Spring Cloud - 31. Kafka Topic 적용 Config// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-clientimplementation 'org.mariadb.jdbc:mariadb-java-client' create table users( id int auto_increment primary key, user_id varchar(20), pwd varchar(20), name varchar(20), created_at datetime default NOW()); Kafka Connect 설치curl -O http://packages.confluent.io/archive/5.5/confluent-community-5.5.2-2.12.tar.gz curl -O http://packages.confluent.io/archive/6.1/confluent-community-6.1.0.tar.gz tar xvf confluent-community-6.1.0.tar.gz

0

Spring Cloud - 31. Kafka Topic 적용

목차 Spring Cloud - 33. Order Kafka Producer Spring Cloud - 32. kafka-mariadb 연결 Spring Cloud - 31. Kafka Topic 적용 Catalog Service// https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafkaimplementation 'org.springframework.kafka:spring-kafka' @EnableKafka@Configurationpublic class KafkaConsumerConfig { @Bean public ConsumerFactory<String, String> consumerFactory(){ Map<String, Object> properties = new HashMap<>(); properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092"); properties.put(ConsumerConfig.GROUP_ID_CONFIG, "consumerGroupId"); properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return new DefaultKafkaConsumerFactory<>(properties); } @Bean public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(){ ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory = new ConcurrentKafkaListenerContainerFactory<>(); kafkaListenerContainerFactory = new ConcurrentKafkaListenerContainerFactory<>(); kafkaListenerContainerFactory.setConsumerFactory(consumerFactory()); return kafkaListenerContainerFactory; }} Order Service@Configurationpublic class KafkaProducerConfig { @Bean public ProducerFactory<String, String> producerFactory(){ Map<String, Object> configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092"); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); return new DefaultKafkaProducerFactory<>(configProps); } @Bean public KafkaTemplate<String, String> kafkaTemplate(){ return new KafkaTemplate<>(producerFactory()); }} @Service@Slf4j@RequiredArgsConstructorpublic class KafkaProducer { private final KafkaTemplate<String, String> kafkaTemplate; public OrderDto send(String kafkaTopic, OrderDto orderDto){ ObjectMapper mapper = new ObjectMapper(); String jsonInString = ""; try{ jsonInString = mapper.writeValueAsString(orderDto); }catch (JsonProcessingException e){ e.printStackTrace(); } kafkaTemplate.send(kafkaTopic, jsonInString); log.info("Kafka Producer send data from the Order microservice: " + orderDto); return orderDto; }}

0

Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현

목차 Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현 Spring Cloud - 29. Micro Service간 통신 Feign client 예외 처리 Spring Cloud - 28. Micro Service간 통신 Feign client Spring Cloud - 27. Micro Service간 통신 ErrorDecoder 구현public class FeignErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { switch (response.status()) { case 400: break; case 404: if (methodKey.contains("getOrders")) { return new ResponseStatusException(HttpStatus.valueOf(response.status()) , "User's orders is empty."); } break; default: return new Exception(response.reason()); } return null; }} @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients@Slf4jpublic class UserServiceApplication { public static void main(String[] args){ SpringApplication.run(UserServiceApplication.class, args); } @Bean public BCryptPasswordEncoder passwordEncoder() throws UnknownHostException { return new BCryptPasswordEncoder(); } @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } @Bean public FeignErrorDecoder getFeignErrorDecoder(){ return new FeignErrorDecoder(); }} token: expiration_time: 84600000 secret: user_token_native_application_changedgateway: ip: 192.168.123.106order_service: url: http://ORDER-SERVICE/order-service/%s/orders exception: orders_is_empty: User's orders is empty. @Component@RequiredArgsConstructorpublic class FeignErrorDecoder implements ErrorDecoder { private final Environment env; @Override public Exception decode(String methodKey, Response response) { switch (response.status()) { case 400: break; case 404: if (methodKey.contains("getOrders")) { return new ResponseStatusException(HttpStatus.valueOf(response.status()) , env.getProperty("order_service.exception.orders_is_empty")); } break; default: return new Exception(response.reason()); } return null; }}