Null 체크 메소드
Objects.isNull(shortestPath) 로 객체가 null인지 true, false로 반환해준다.
Objects.isNull(shortestPath) 로 객체가 null인지 true, false로 반환해준다.
배경 NPE가 발생하고 있는 이슈가 발생해서 로그를 확인하니, 아래와 같이 로그가 남아있고 stack trace가 로깅되어 있지 않았다. java.lang.NullPointerException 원인 JVM에서 최초로 발생하는 예외를 출력하고, 이를 기억해뒀다가 자주 발생하는 것이 확인되면 더 이상 stack trace는 출력하지 하지않도록 최적화가 이루어지고 있다. 이를 기능을 사용하지 않고, 항상 stack trace를 출력하려면 아래와 같이 JVM 옵션을 추가해야된다. -XX:-OmitStackTraceInFastThrow 참고 자료 https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace
아티클 마다 NIC(Network Interface Card)를 설명하는 계층이 달라서 혼란스러웠다. NIC가 어떤 레이어인지 정리해보자. NIC는 1계층이다? 1계층의 목적인 전기신호를 데이터로 변환하는 역할을 NIC가 하고있다. 따라서, NIC는 1계층 장비이다. NIC는 2계층이다? NIC에는 MAC 주소가 있기 때문이다. MAC 주소는 2계층에서 다루어지는 주소다. MAC 주소를 기반으로 전송 받은 데이터를 다음 레이어로 보낼지 말지 선택하기 때문에 2계층이다. 결론 나는 1계층 및 2계층이라고 결론을 지을 것이다. 자료를 검색할수록 각자 다른 결론을 내고 있기 때문에 정답이 없는 문제라고 생각한다....
배경 배포 스크립트를 읽어보는 중 kill -USR1 `cat {nginx pid 파일 경로}` 라는 내용이 있었다. 해당 명령의 의미를 이해해본다. signal 종류 kill 명령어를 통해서 특정 프로세스에 signal을 보낼 수 있다. 그 중 SIGUSR1, SIGUSR2는 커스터마이징이 가능한 signal이다. 참고: https://man7.org/linux/man-pages/man7/signal.7.html pid 파일 nginx는 자신의 pid(프로세스 id)를 파일 형태로 저장해둔다. http://nginx.org/en/docs/ngx_core_module.html#pid 따라서 위 명령어는 nginx 프로세스에 SIGUSR1 signal을 보내는 것이다. Nginx가 SIGUSR1 signal을 받은 경우 Nginx에서는 각 signal에 따른 동작이 미리 정의되어 있다....
HTTP multipart를 이용한 파일 업로드 API를 구현했는데 NGINX에서 거부를 하는 문제가 발생했다. 기본적으로 NGINX의 최대 HTTP body 크기는 1MB다. http { ... client_max_body_size 100M; } 또는 server { ... client_max_body_size 100M; } 와 같은 방법으로 파일 용량 제한을 수정할 수 있다. 참고 자료 https://www.tecmint.com/limit-file-upload-size-in-nginx/ https://stackoverflow.com/questions/26717013/how-to-edit-nginx-conf-to-increase-file-size-upload
Nginx 설치 소스 다운로드 wget http://nginx.org/download/nginx-1.12.2.tar.gz tar xvzf nginx-1.12.2.tar.gz 빌드 cd nginx-1.12.2 ./configure --prefix=/home/{user}/apps/nginx-1.12.2 --user={user} --group={user} --error-log-path=/home/{user}/logs/nginx/error.log --http-log-path=/home/{user}/logs/nginx/access.log --without-http_rewrite_module --without-http_scgi_module --without-http_uwsgi_module --without-http_fastcgi_module make make install 옵션 설명 OS 사용자 -user : OS 사용자 계정. irteam을 권장한다. -group=irteam : OS 사용자의 그룹. irteam을 권장한다. 파일 경로 -prefix : Nginx 빌드 결과물이 들어갈 디렉토리 -error-log-path: 에러 로그를 저장할 경로 -http-log-path : 액세스 로그를 저장할 경로 모듈 선언 -without-http_scgi_module : http_scgi_module 모듈 제외 -without-http_uwsgi_module : http_uwsgi 모듈 제외 -without-http_fastcgi_module : fastcgi_module 모듈 제외 -without-http_rewrite_module : http_rewrite_module 제외 -with-http_ssl_module : HTTPS로 서비스하기 위한 모듈 추가 -with-http_v2_module : HTTP/2로 서비스하기 위한 모듈 추가 그 외 다양한 옵션...
크게 2가지 파일을 만들어줘야된다. 1. Dockefile FROM nginx COPY nginx.conf /etc/nginx/nginx.conf COPY fullchain.pem /etc/letsencrypt/live/hy-notice.kro.kr/fullchain.pem COPY privkey.pem /etc/letsencrypt/live/hy-notice.kro.kr/privkey.pem nginx.conf: nginx의 설정을 저장해두는 파일이다. fullchain.pem, privkey.pem: TLS를 설정하면서 생성한 인증서다. 2. nginx.conf events {} http { upstream app { server 172.17.0.1:8080; } server { listen 80; server_name hy-notice.kro.kr; return 307 https://$host$request_uri; } server { listen 443 ssl; server_name hy-notice.kro.kr; ssl_certificate /etc/letsencrypt/live/hy-notice.kro.kr/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/hy-notice.kro.kr/privkey.pem; # Disable SSL ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 통신과정에서 사용할 암호화 알고리즘 ssl_prefer_server_ciphers on; ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!...
목표 새로 만든 MySQL 서버에 기존에 있던 MySQL 스키마를 그대로 가져오고 싶었다. 해결 방법 mysql에서 제공하는 mysql client에서 db 데이터를 dumping하는 기능을 제공하주고 있다. --no-data는 테이블 내에있는 row 들은 복제하지 않겠다는 옵션이다. mysqldump --user=<username> --host=<hostname> --password --no-data <original db> | mysql -u <user name> -p <new db> mysqldump와 관련된 추가적인 옵션들은 아래 링크에서 확인 가능하다. https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html 참고 자료 https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html https://dev.mysql.com/doc/refman/8.0/en/mysql-batch-commands.html https://www.prisma.io/dataguide/mysql/short-guides/exporting-schemas
배경 객체의 상태를 변화시키면서 같은 메서드를 호출하는 경우 Mockito로 검증하는데 계속 실패했다. class MutableService(val mutableMessageQueue: MutableMessageQueue) { fun run() { val mutable = Mutable() val nextNames = listOf("a", "b", "c", "d") for (nextName in nextNames) { mutable.name = nextName mutableMessageQueue.publish(mutable) } } } class MutableMessageQueue { fun publish(mutable: Mutable?) { if (mutable != null) { println(mutable.name) } } } data class Mutable(var name: String = "") class MutableTest { lateinit var mutableMessageQueue: MutableMessageQueue lateinit var mutableService: MutableService @Test fun test() { // given mutableMessageQueue = mock() mutableService = MutableService(mutableMessageQueue) // when mutableService....
multipart 를 통해 파일을 body로 보내는데, 이미지라서 바이트 데이터로 보내게된다. rest-docs에서는 이를 굳이 보여줄 필요가 없기 때문에, body내용을 바꾸는 기능이 필요했다. 이를 위해서는 preprocessRequest 라는 것을 사용하면 된다. // MockMvc 테스트 중에 .andDo(document("save-image", preprocessRequest(new ImageBodyPreprocessor()), responseFields( fieldWithPath("[]").description("파일 경로")))); public class ImageBodyPreprocessor implements OperationPreprocessor { private static final String FILE = "file"; private static final OperationRequestPartFactory PART_FACTORY = new OperationRequestPartFactory(); private static final OperationRequestFactory REQUEST_FACTORY = new OperationRequestFactory(); @Override public OperationRequest preprocess(final OperationRequest request) { List<OperationRequestPart> parts = new ArrayList<>(); for (OperationRequestPart part : request....
@ExtendWith(MockitoExtension.class) public class BoTest { @Mock private Dao dao; @InjectMocks private Bo bo; @Test public void test() { given(dao.findById(anyLong())) .willReturn(new Object()); } }
Mocking한 상태인 orderTableDao 에서 orderTableDao.save(entity) 를 호출했을 때, entity의 필드값을 검증할 필요가 있었다. argThat() 메서드를 사용하면 검증이 가능하다. verify(orderTableDao, times(3)).save( argThat(orderTable -> !orderTable.isEmpty() && Objects.nonNull(orderTable.getTableGroupId()) ) ); 참고 자료 https://stackoverflow.com/questions/31993439/can-mockito-verify-an-argument-has-certain-properties-fields https://www.baeldung.com/mockito-argument-matchers
아래와 같이 람다 형식으로 특정 모드를 mocking 할 수 있다. val mock = mock<MyClass> { on { getText() } doReturn "text" } 참고 자료 https://github.com/mockito/mockito-kotlin
문제 상황 Spring MVC의 컨트롤러에서 요청 보낸 사람의 IP를 확인하기 위해서 HttpServletRequest를 파라미터로 받고 있다. @PutMapping fun put(request: HttpServletRequest) { // ... request.remoteAddr // ... } 단위 테스트를 작성할 때, HttpServletRequest를 인자로 넘겨야되는데, 이는 인터페이스라서 직접 구현해야되는 상황이 발생했다. 해결 방법 Spring Test에서는 MockHttpServletRequest를 제공해주고 있다. 이는 HttpServletRequest의 구현체다. @Test fun test() { controller.put(MockHttpServletRequest()) } 참고 자료 How to Mock HttpServletRequest | Baeldung java - How to extract IP Address in Spring MVC Controller get call?...
JavaScript에서 비디오 스트림을 캡쳐하여 분석 ,가공 저장 등을 가능하게 해준다. 예제를 기반으로 사용법을 익혀본다. const canvas = document.querySelector("canvas"); // Optional frames per second argument. const stream = canvas.captureStream(25); const recordedChunks = []; console.log(stream); const options = { mimeType: "video/webm; codecs=vp9" }; // 1 const mediaRecorder = new MediaRecorder(stream, options); mediaRecorder.ondataavailable = handleDataAvailable; // 2 mediaRecorder.start(); function handleDataAvailable(event) { console.log("data-available"); if (event.data.size > 0) { recordedChunks.push(event.data); console.log(recordedChunks); download(); } else { // … } } function download() { const blob = new Blob(recordedChunks, { type: "video/webm" }); // 3 const url = URL....
소개 Maven이란?: 빌드 툴 빌드란?: 아래의 4가지 과정이 존재한다. 컴파일: java코드를 JVM에 올려서 인터프리터가 이해할 수 있는 수준으로 바이너리로 바꿔주는 과정 테스트: 소스코드 컴파일 -> 테스트 코드 컴파일 -> 테스트 실행 패키징: jar나 war 같은 형식으로 압축 배포: 패키징한 것을 remote 저장소에 배포 일반적으로 배포 기능은 안쓰고 Jenkins 같은 외부 툴 사용 Maven 설정 디렉토리 구조(Java web project) pom.xml: maven 설정이 포함되어 있다. /src/main/java: java source code /src/main/resources: 배포할 환경 설정 파일(xml, properties) /src/main/webapp: 웹자원 (JSP, JS, CSS 등) /src/test/java: test source code /src/test/resource: test 환경의 설정 파일 /target: 컴파일 된 결과물이 저장되는 경로 프로젝트마다 구조가 조금씩 다를 수 있다....
배경 2개의 프로젝트 사이에 의존 관계를 갖고 있을 때, 로컬에서 빌드하는 방법을 몰랐다. Maven Repository Maven은 3가지 종류의 레포지토리를 지원한다. local: 로컬 장치의 폴더 central: Maven 커뮤니티에서 제공하는 레포지토리 remote: 어떤 조직의 커스텀 레포지토리 Maven에서는 빌드 했을 때 프로젝트를 로컬에서 재사용 할 수 있도록 로컬에 저장한다. 해결 방법 의존이 되는 프로젝트를 먼저 local repository에 배포하고 빌드하면 성공적으로 빌드된다. mvn install # 빌드 후 local repository에 배포 참고 자료 https://www.baeldung.com/maven-local-repository
배경 팀내에 사용하고 있는 컴포넌트 간에 어떤 의존 관계를 가지고 있는지, 의존 관계에 사이클이 존재하지 않는지 확인이 필요했다. 문법 mvn dependency:tree -Dincludes=[groupId]:[artifactId]:[type]:[version] -Dincludes 또는 -Dexcludes 옵션을 통해 자신이 원하는 의존만 필터링해서 볼 수 있다. groupId, artifactId, type, version 중에 자신이 필터링을 원치 않는 값은 빈값으로 해두면 되고, 와일드 카드도 사용할 수 있다. org.apache.*::: 참고 자료 https://maven.apache.org/plugins/maven-dependency-plugin/examples/filtering-the-dependency-tree.html
MariaDB는 기본 캐릭터셋으로 utf8mb4를 쓰기 때문에 스키마 생성시 utf8을 사용하게 설정해야된다. CREATE DATABASE practice DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
먼저 자신이 필요한 버전을 찾는다. 아래 링크에서 찾을 수 있다. https://downloads.mariadb.org/mariadb/repositories/ 이 글은 MariaDB 10.6을 기준으로 진행한다. MariaDB 레포지토리를 내 Ubuntu 시스템에 등록한다. sudo apt-get install software-properties-common sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc' sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] https://mirror.yongbok.net/mariadb/repo/10.6/ubuntu bionic main' MariaDB 10.6을 설치한다. sudo apt update sudo apt install mariadb-server 참고 자료 https://www.linuxbabe.com/mariadb/install-mariadb-10-5-ubuntu