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

문법 [ {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

쿠버네티스로 클러스터 구성하기

쿠버네티스 구성 방법 퍼블릭 클라우드 업체에서 제공하는 관리형 쿠버네티스 EKS(Amazon Elastic Kubernetes), AKS(Azure Kubernetes Services), GKE(Google Kubernetes Engine) 등이 있다. 구성이 이미 다 갖춰져 있고 마스터 노드를 클라우드 업체에서 관리하기 떄문에 학습용으로는 적합하지 않다. 설치형 쿠버네티스 Rancher, OpenShift 등이 있다. 하지만 유료라는 단점이 있다. 구성형 쿠버네티스 kubeadm, kops(Kubernetes Operations), KRIB(Kubernetes Rebar Integrated Bootstrap), kubespray 등이 있다. 사용하는 시스템에 쿠버네티스 클러스터를 자동으로 구성해준다. kubeadm이 가장 널리 알려져 있다. kubeadm은 사용자가 변경하기도 쉽고, 온프레미스와 클라우드를 모두 지원하며, 배우기도 쉽다....

2024-09-15 · 3 min · 441 words

코딩을 지탱하는 기술

프로그래밍 언어의 목적 프로그래밍 언어는 인간을 편하게 하기 위해 만들어졌다. 예전에는 케이블의 연결을 바꾸고(ENIAC), 테이프에 구멍을 뚫어서 데이터를 표현하는(EDSAC) 것을 통해 프로그램을 변경할 수 있었다. 이것은 사람이 프로그램을 읽거나 쓰기에는 어려웠다. 그 이후에 현재 우리가 사용하고 있는 것과 비슷한 프로그래밍 언어 FORTRAN이 고안되었다. 이렇게 프로그래밍 언어의 목적은 프로그래밍을 편리하게 하는 것이다. 하지만 이 편리함의 의미는 사람에 따라 다르기 때문에 현재에 수많은 언어가 존재하고 있는 것이다. 예를 들어, C++는 빠른 실행 속도를 중시하고 있지만, 결과적으로 언어 사양이 복잡해졌다....

2024-09-15 · 10 min · 2026 words

컬럼명 수정 SQL

ALTER TABLE user CHANGE name nickname VARCHAR(30) NULL; 바꿀 컬럼명 뒤에 타입도 함께 명시하지 않으면 에러가 난다는 점을 유의해야된다.

2024-09-15 · 1 min · 19 words

컨테이터 IP 확인하기

docker위에서 돌아가고 있는 WAS가 docker 위에 올라가 있는 DB에게 연결을 계속 실패하는 문제가 있었다. 분명 docker inspect 명령어로 확인한 IP 주소 값인데 왜 연결에 실패하는지 이해가 되지 않았다. docker를 사용하면 docker0라는 네트워크 인터페이스가 생긴다. docker에서는 이 네트워크 인터페이스에 bridge라는 네트워크 드라이버를 제공하고있다. 그래서 docker를 설치한 직후에 docker network ls 명렁어를 입력하면 아래와 같이 bridge 드라이버를 사용중인 네트워크를 볼 수 있다. 또한 아무 설정없이 컨테이너를 만들면 이 기본 bridge 네트워크에 연결하게 된다....

2024-09-15 · 1 min · 134 words

컨테이너로 이미지 만들기 및 Docker hub에 업로드

컨테이너로 이미지 만들기 docker commit [ContainerID] 이미지에 태그 붙이기 docker tag SOURCE_IMAGE[:TAG] TARcoGET_IMAGE[:TAG] 이미지 docker hub에 업로드 및 다운로드 docker push [OPTIONS] NAME[:TAG] docker pull [OPTIONS] NAME[:TAG] 참고 자료 https://galid1.tistory.com/324

2024-09-15 · 1 min · 30 words

최대 샤드 개수

배경 테스트 코드 실행 중에 ES에서 최대 샤드 개수를 초과해서 인덱스를 만들 수 없다는 오류가 발생했다. cluster.max_shards_per_node ES 클러스터 설정 값 중에 한 노드 당 만들 수 있는 최대 샤드 수가 있다. 클러스터 내에 있을 수 있는 (프라이머리 샤드 수) + (레플리카 샤드 수)의 최대는 cluster.max_shards_per_node * number of non-frozen data nodes 참고 자료 https://www.elastic.co/guide/en/elasticsearch/reference/current/misc-cluster-settings.html

2024-09-15 · 1 min · 55 words