특정 주기마다 실행되는 메서드

Spring task라는 내장된 기능 통해 특정 메서드를 주기적으로 실행하는 것이 가능하다. 스케쥴링 지원 활성화 Configuration에 @EnableScheduling 어노테이션을 추가하면 활성화된다. @Configuration @EnableScheduling public class SpringConfig { ... } fixed 방식 fixed 방식에는 2가지가 있다. fixedDelay: 작업이 종료된 시간부터 지정된(ms단위) 시간 뒤에 재실행. fixedRate: 작업이 시작된 시간부터 지정된(ms단위) 시간 뒤에 재실행. @Scheduled(fixedDelay = 1000) public void scheduleFixedDelayTask() { System.out.println( "Fixed delay task - " + System.currentTimeMillis() / 1000); } @Scheduled(fixedRate = 1000) public void scheduleFixedRateTask() { System....

2024-09-15 · 1 min · 181 words

특정 문자열로 시작하는 값 조회

방법 CONCAT을 이용해야 된다. SELECT * FROM TABLE_NAME WHERE COLUMN_NAME LIKE CONCAT(‘%’, #{searchKeyword}) 참고 자료 https://dongram.tistory.com/12

2024-09-15 · 1 min · 16 words

특정 메소드가 호출된 횟수 검증

Mockito.verify(utilClass, Mockito.times(1)).method(); https://stackoverflow.com/questions/57775074/mockito-veify-is-giving-wrong-invocation-count

2024-09-15 · 1 min · 3 words

특정 디렉토리의 하위 디렉토리 검색

os.walk() 함수를 사용하면 된다. import os for (path, dir, files) in os.walk("c:/"): for filename in files: ext = os.path.splitext(filename)[-1] if ext == '.py': print("%s/%s" % (path, filename)) 참고 자료 https://wikidocs.net/39

2024-09-15 · 1 min · 30 words

특정 디렉토리의 파일 리스트

import os os.listdir(dirname) 참고 자료 https://wikidocs.net/39

2024-09-15 · 1 min · 6 words

특정 디렉토리에 있는 전체 파일 가져오기

Files.walk 메소드는 현재 입력받은 경로로부터 파일 트리를 전부 탐색해서 stream 으로 반환한다. Files.walk(Paths.get("folder")) .filter(Files::isRegularFile) .forEach(System.out::println); folder\file1.txt folder\file2.txt folder\subfolder\file3.txt folder\subfolder\file4.txt 현재 디렉토리에 있는 파일만 가져올 경우는 list() 메소드를 사용하면 된다. Files.list(Paths.get("folder")) .filter(Files::isRegularFile) .forEach(System.out::println); folder\file1.txt folder\file2.txt https://stackoverflow.com/questions/1844688/how-to-read-all-files-in-a-folder-from-java https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html

2024-09-15 · 1 min · 36 words

특정 경로에 파일이 존재하는 경우에 따른 분기 처리

문법 [ {parameter} {FILE 경로} ] parameter -e: 파일이 존재하면 true 리턴 -f: regular file이 존재하면 true 리턴 -r: 읽기 권한이 있는 파일이 존재하면 true 리턴 -w: 쓰기 권한이 있는 파일이 존재하면 true 리턴 -x: 실행 권한이 있는 파일이 존재하면 true 리턴 -d: 디렉토리가 존재하면 true 리턴 예시 FILE="$1" if [ -f "$FILE" ]; then echo "File $FILE exist." else echo "File $FILE does not exist" >&2 fi 참고 자료 https://www....

2024-09-15 · 1 min · 71 words

특정 Endpoint만 Interceptor를 거치도록 구현

아래와 같이 인터셉터를 추가할 때 addPathPatterns 를 통해 일부 경로에 대해서만 인터셉터를 처리할 수 있다. @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AdminAccessInterceptor(administratorService)) .addPathPatterns("/api/games/**", "/api/tags/**", "/api/sliders/**", "/api/admins/**"); } } 하지만 WAS가 HTTP를 받았을 때 항상 IP가 웹 서버(NGINX)의 IP로 인식되는 문제가 있었다. 문제 해결 WAS는 중간에 있는 리버스 프록시의 IP를 인식하는 문제가 있었다. 보통 요청을 보낸 Client의 IP 를 확인하기 위해서 X-Forwarded-For 헤더를 사용한다고 한다....

2024-09-15 · 1 min · 186 words

트랜잭션 스크립트 패턴 vs 도메인 모델 패턴

배경 팀 코드를 읽어보게 되었는데, 트랜잭션 스크립트 패턴에 가까운 형태를 취하고 있었다. 지금까지 도메인 모델 패턴을 사용해온 입장에서는 상당히 절차지향 적이고 유지보수하기 힘들어보였는데, 이 두 가지 패턴이 어떤 장단점이 있는지 비교해본다. 개인적인 견해는 강조 표시로 표기했다. 트랜잭션 스크립트 패턴 프레젠테이션 레이어에서 오게되는 단일 요청별로 처리되는 메서드가 각각 존재하는 패턴이다. 비즈니스 로직을 처리하는 하나의 기능을 스크립트라고 표현한다. 일반적으로 유스케이스 하나당 하나의 클래스를 가진다. class BuyProductBO { List<Product> getProducts(string filter) { /* some database query */ } void putInCart(int productId, int amount) { ....

2024-09-15 · 2 min · 317 words

트랜잭션 관련 주의점

배경 @Transactional을 붙여놓은 테스트에서 XXXRepository로 저장한 데이터들이 Awaitility 사용시에만 조회되지 않는 이슈가 있었다. Awaitility.await().pollInterval(100, TimeUnit.MILLISECONDS).until({ XXXRepository.listByIds(ids) }) { it.size == 5 } 원인 Awaitility는 기본적으로 별도의 스레드로 동작하기 때문에 위와 같은 예시에서는 같은 트랜잭션으로 묶이지 않는다. 테스트와 같은 스레드에서 동작하기를 원한다면 pollInSameThread()를 사용해야 된다. Awaitility.with().pollInSameThread().await().atMost(1000, TimeUnit.MILLISECONDS).until { XXXRepository.listByIds(ids).size == 5 } 참고 자료 https://github.com/awaitility/awaitility/wiki/Usage#thread-handling

2024-09-15 · 1 min · 54 words

통합 테스트에서 사용되는 패턴

이 글은 아래 글을 한국어로 정리한 것이다. https://medium.com/swlh/usual-production-patterns-applied-to-integration-tests-50a941f0b04a Basics: build, operate, check 모든 통합테스트는 build, operate, check 단계를 가진다. build: 테스트 시나리오를 준비하는 단계. 일반적으로 데이터에비스에 데이터를 추가한다. operate: 테스트할 API나 객체의 메서드를 호출한다. check: 실행한 API나 메서드의 결과가 예상한대로 동작했는지 확인한다. build 단계: 데이터 추가 build 단계에 테스트 시나리오를 위해 데이터베이스에 데이터를 추가해야된다. 데이터를 추가하는 방법으로는 3가지가 있다. 테스트 중인 시스템의 API 호출 순수 SQL 사용 Repository Layer 사용 테스트 중인 시스템의 API 호출 이는 안티패턴이 될 수 있다....

2024-09-15 · 2 min · 343 words

통합 테스트에 @Transactional을 붙이면 안되는 이유

배경 통합 테스트중에 DB에 추가된 데이터를 제거하기 위해서 @Transactional을 사용하는 경우가 있다. 이럴 경우 버그가 있어 실패해야될 테스트가 통과되어버리는 문제가 발생할 수 있다. @SpringBootTest @Transactional internal class CartItemsControllerTests { ... @Test fun getAllCartItems() { val cart = Cart() cart.addProduct(product, 3) cartsRepository.save(cart) mockMvc.perform(get("/carts/{id}/items", cart.id)) .andExpect(status().isOk) .andExpect(jsonPath("$.[*].id").value(hasItem(cart.items[0].id.toString()))) .andExpect(jsonPath("$.[*].product.id").value(hasItem(product.id.toString()))) .andExpect(jsonPath("$.[*].product.name").value(hasItem(product.name))) .andExpect(jsonPath("$.[*].quantity").value(3)) } } 이유 서비스에 @Transactional이 붙어있지 않아서 실패되어야할 요청이 통과되어버린다. lazy loading이 동작하지 않아서 실패해야 되는 경우에도 통과 되어버린다. 변경 탐지가 동작하지 않아서 실패해야 되는 경우에도 통과 되어버린다....

2024-09-15 · 1 min · 107 words

테스트 코드도 jar에 포함 시키기

배경 여러 컴포넌트의 테스트 코드에서 테스트 픽스처로 공통적으로 구성되어야 하는 로직이 있었다. 이를 core 컴포넌트의 테스트 코드(/src/test)에 구현하고 모든 컴포넌트에서 사용할 수 있도록 하고 싶었다. Apache Maven JAR Plugin maven에서 jar로 package할 때 사용되는 기본 플러그인 해당 플러그인의 execution을 추가해서 테스트 코드를 별도의 jar 파일로 패키징할 수 있다. <execution>: maven 라이프사이클 중에 플러그인이 실행하고자 하는 내용을 정의할 수 있다. 테스트 코드를 추가하고자 하는 컴포넌트의 maven 설정 아래와 같이 설정하면 프로덕션 코드는 {artifactId}-{version}....

2024-09-15 · 1 min · 101 words

테스트 종류

최근 수업 시간을 통해 단위 테스트나 통합 테스트 같은 코드 동작 테스트만 있는게 아니라 부하 테스트, 성능 테스트도 필요하다는 사실을 알게되었다. 이외에도 어떤 테스트가 있고 우리는 개발에 어떤 테스트가 필요한지 알아보도록한다. 테스트 접근법 테스트를 하기 위해서는 3가지의 접근법이 있다고한다. 화이트 박스 테스트 애플리케이션 내부의 코드를 기반으로 테스트를 진행한다. 모든 코드 라인을 거쳐 변수에 의도한 값이 저장되어 있는지, 의도한대로 분기문을 거치는지 등의 개발자가 이해할 수 있는 수준의 테스트 코드를 작성한다. 화이트 박스 테스트의 경우 불필요한 코드를 제거하는 등의 최적화를 할 수 있는 장점이 있다....

2024-09-15 · 2 min · 349 words

테스트 실행 시 로그 확인하기

gradle test -i 참고 자료 https://stackoverflow.com/questions/3963708/gradle-how-to-display-test-results-in-the-console-in-real-time

2024-09-15 · 1 min · 6 words

테스트 방법론

구현과 테스트 소프트웨어 생명 주기 (SDLC)와 테스트 V-Diagram Requirement Analysis: 의뢰자로부터 요구 사항을 받아 분석 System Design: 소프트웨어 명세 작성. 시스템 구성, 메뉴 구조, 데이터 구조 등. Architecture Design: 모듈 목록, 각 모듈의 기능, 인터페이스 관계, 의존 관계, 데이터베이스 테이블 등 설계. Module Design: 프로그래머가 직접 코딩을 할 수 있도록 유닛 또는 모듈 단위로 설계. 테스트 unit test 테스트가 가능한 최소 단위로 나누어진 모듈, 프로그램, 객체, 클래스 내에서 결함을 찾고 그 기능을 검증하는 것임 시스템의 다른 부분에서 격리하여 독립적으로 수행 됨 코드를 작성한 프로그래머가 주도함 코드를 중심으로 수행함 integration test 모듈간 인터페이스를 테스트 함 각기 다른 모듈과 상호 연동하는 동작을 테스트 함 기능적 특성과 특정한 비기능적 특성을 테스트 함 기능성: 조향 비기능성: 성능, 부하, 스트레스 등 system test 전체 시스템 또는 제품의 동작에 대해 테스트함 가능한 범위에서 실제 최종 사용 환경 또는 유사한 환경에서 수행함 환경 특성 장애 리스크를 최소화 하기 위해서 기능 및 비기능 요구 사항을 모두 검증함 독립적인 테스트 팀이 주도함(QA 팀 등) acceptance test 시스템이 실제 사용할만한 준비가 되어있는지에 대해 평가함 결함을 찾는 것이 주된 관심사가 아님 사용자가 불편함이 없는지 확인하는 것이 주 목적 시스템을 사용하는 사용자가 전담하여 수행함 알파/베타 테스트 알파 테스트: 사내 이해당사자들에게 테스트 베타 테스트: 사외 대상자들에게 테스트 FIRST: 좋은 단위 테스트는 무엇인가?...

2024-09-15 · 4 min · 750 words

타입 소거

목표 타입 소거의 의미를 이해한다. 타입 소거가 필요한 이유를 이해한다. 타입 소거의 의미 타입 파라미터에 대해서 컴파일 타임에만 타입 검사를 하고 런타임에는 타입에 대한 정보를 삭제하는 방식 아래와 같은 코드가 있다고 가정하면 public static <E> boolean containsElement(E [] elements, E element){ for (E e : elements){ if(e.equals(element)){ return true; } } return false; } 런타임에는 아래와 같이 동작한다. unbound 타입인 E가 Object 타입으로 치환되었다. public static boolean containsElement(Object [] elements, Object element){ for (Object e : elements){ if(e....

2024-09-15 · 2 min · 244 words

클래스의 모든 필드를 getter 없이도 직렬화

코드 리뷰를 하다가 새로운 사실을 알게 되었다. Jackson은 기본적으로 getter 메소드로 직렬화를 한다. 아래처럼 지정하면 클래스의 필드들을 기반으로 접근제어자 상관 없이 직렬화가 가능하다. OBJECT_MAPPER.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

2024-09-15 · 1 min · 25 words

클라이언트가 서버에 갱신된 정보를 빠르게 가져오는 방법

이런 고민을 한 이유: HTTP의 한계 무상태성: 클라이언트의 상태를 기억하지 않기 때문에 클라이언트에게 갱신된 정보를 알려줄 수 없다. 비연결성: 1개의 리퀘스트로 부터 응답을 하면 연결이 끊어진다. Polling: 주기적으로 클라이언트가 request를 보내 새로운 정보를 갱신하는 방법 장점 구현이 단순하다. 단점 주기적으로 HTTP 연결을 맺고 끊는게 상당한 클라이언트와 서버 측에서 모두 큰 부담이 된다. 이를 어느 정도 해결하기 위해 전송하는 데이터 양을 줄이기 위해 ajax를 사용한다. Long Polling(HTTP/1.1): 클라이언트와 서버가 계속 연결을 맺고 끊는 것을 줄이기 위해서 만든 방법....

2024-09-15 · 2 min · 401 words

쿼리 파라미터가 여러 개인 경우 어떻게 처리할까

일반적으로 두 가지 방법을 사용한다. http://localhost:8080/api/foos?id=1,2,3 http://localhost:8080/api/foos?id=1&id=2 위의 두 예시모두 Spring MVC에서 자동으로 매핑시켜주기 때문에 걱정없이 사용할 수 있다. @GetMapping("/api/foos") @ResponseBody public String getFoos(@RequestParam List<String> id) { return "IDs are " + id; } 참고 자료 https://stackoverflow.com/questions/2602043/rest-api-best-practice-how-to-accept-list-of-parameter-values-as-input

2024-09-15 · 1 min · 37 words