Docker IP 대역 변경
·
Trouble Shooting
IP 대역을 변경해야 하는 이유우리 회사의 서비스는 Docker 기반이며 고객사 서버에 배포하는 방식으로 구축한다.어느 한 고객사에도 마찬가지로 Docker 기반으로 배포하여 개발을 하던 중 고객사에서 우리가 사용 중인 특정 IP 대역을 다른 IP 대역으로 변경해 달라는 요청이 들어왔다. 특정 IP 대역은 서비스를 배포한 Docker에서 사용 중이었기 때문에 Docker의 IP 대역을 변경해야 한다는 뜻이었다. Docker의 IP 대역을 변경해본 적이 없어서... 가능한건가? 라는 생각이 들었지만 여러 블로그 글들을 보며 해결하여 사내에도 공유했다. 요청사항`172.17.x.x` `172.18.x.x` 대역 모두 고객사 사내망에서 사용 중이므로 변경요청변경할 IP 대역 : `100.64.x.x` 서비스..
Gson으로 파싱할 때 double 자동변환
·
Trouble Shooting
회사에서 테스트하던 도중 데이터를 확인했는데 정수형(`Integer`)이어야 할 데이터가 실수형(`Double`)으로 들어가 있었다. 분명 클라이언트에서 넘어오는 파라미터도 정수형이었는데 뭐가 문제일까? String json = "{\"params\":{\"num\":9999}}";Host host = gson.fromJson(json, Host.class);코드를 추적해보니 json 문자열 파라미터를 전달받은 후 `gson`을 이용해 객체로 변환할 때 객체의 필드가 실수로 변환되고 있었다. 원인`Gson`의 Object to Number 정책의 기본값이 `Double`이므로 따로 설정을 세팅해주지 않으면 `Double`로 변환되는 것이었다. 해결Gson을 커스텀해서 따로 util 클래스로 만들어 ..
CAS (Compare And Swap)
·
Computer Science
비동기적 동시성 제어 기법 중 하나로, 여러 스레드가 동시에 데이터를 수정하려고 할 때 데이터의 일관성을 보장하는 방법이다. CAS는 원자적 연산(atomic operation)으로, 멀티스레드 환경에서 안전하게 데이터를 읽고 쓸 수 있게 한다. 원자적 연산컴퓨터 과학에서 사용하는 원자적 연산(atomic operation)은 해당 연산이 더 이상 나눌 수 없는 단위로 수행된다는 것을 의미한다. int i = 0;위와 같은 변수가 있을 때 `i = 1`이 연산은 둘로 쪼갤 수 없는 원자적 연산이다.단 하나의 순서로 실행되기 때문이다.오른쪽에 있는 1을 왼쪽의 `i`변수에 대입한다. `i = i + 1`하지만 이 연산은 원자적 연산이 아니다.다음 순서로 나누어 실행되기 때문이다.오른쪽에 있는 `i`의 값을..
상속이 캡슐화를 위반하는 이유
·
Object-oriented programming
객체지향 공부를 하던 중 객체지향의 특징인 상속이 캡슐화를 위반한다는 글을 보게 되었다.결론부터 말하자면 상속이 항상 캡슐화를 위반하는 것은 아니다.그럼 어떤 상속이 캡슐화를 위반할까?한번 알아보자. 상속객체 지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)받고 거기에 더해 필요한 특성을 추가하는 것.즉, 확장해서 사용할 수 있다는 뜻이다. 캡슐화객체의 데이터와 동작을 하나로 묶어 실제 구현 내용을 외부에 공개하지 않는 것즉, 객체가 내부적으로 기능을 어떻게 구현하는지를 감추는 것이다. 상속과 캡슐화는 객체지향의 4대 특성이다.하지만, 아이러니하게도 상속은 캡슐화를 위반한다. 예제를 통해 알아보자.// Broken - Inappropriate use of inheritance!p..
hashCode()와 equals()를 같이 오버라이딩해야 하는 이유
·
Java
`hashCode()`와 `equals()`는 Object 클래스에 선언된 메서드며, 모든 객체가 사용할 수 있다.두 메서드는 같이 오버라이딩해야 하는 이유가 있다. 그 이유를 알아보자. 일단 `hashCode()`와 `equals()`부터 알아보자. JDK 21 기준 public class Object { @IntrinsicCandidate public native int hashCode(); public boolean equals(Object obj) { return (this == obj); }} hashCode()객체의 해시 값을 반환한다.메모리 주소와 무관한 난수 값을 사용해서 해시를 생성하는 해시 함수다. 해시가 뭘까? 해시 함수부터 알아보자. 해시 함수..
ConcurrentHashMap의 동작원리
·
Java
ConcurrentHashMap은 실무에서 처음 사용해 봤었다. 코드리뷰를 진행하다가 HashMap을 사용한 코드에 대해서 Thread-Safe한 HashMap인 ConcurrentHashMap을 사용해야 한다고 피드백을 받았다. 그 후로 동시성 문제가 발생할 만한 곳은 ConcurrentHashMap을 사용하여 해결했다.그러다 문득 동시성 문제를 어떻게 해결하는지 궁금해져서 파헤쳐 보기로 했다. JDK 21 기준 일단 키-값 쌍 구조인 HashTable을 구현한 클래스는 크게 3가지가 있다.HashMap (Thread-Unsafe)HashTable (Thread-Safe)ConcurrentHashMap (Thread-Safe) ConcurrentHashMap이 Thread-Safe한 HashMap..
제네릭 타입 소거 (Type Erasure)
·
Java
제네릭은 타입 안정성을 보장하기 위해 JDK 1.5부터 도입됐으며, 이전 자바 버전의 코드와의 호환성을 위해 컴파일 타임에 제네릭 타입은 사라진다.제네릭의 작동 방식public class Node { private T data; private Node next; public Node(T data, Node next) { this.data = data; this.next = next; } public T getData() { return data; } // ...}제네릭 타입인 `Node` 클래스는 컴파일되면 아래 코드로 변환된다. public class Node { private Object data; private Node next; ..
ArrayList는 왜 제네릭 타입 E[]가 아닌 Object[]으로 요소를 관리할까?
·
Java
제네릭을 공부하다가 제네릭을 사용하는 클래스 중 대표적으로 자주 사용되는 ArrayList에 대해 파헤치기 시작했다.그런데 이상한 점을 하나 발견했다.내부 요소들을 관리하는 자료구조가 Object[] 이었던 것이다.ArrayList가 제네릭을 사용하니 E[] 를 사용해도 될 것 같은데, 왜 Object[] 를 사용하고 있을까? ArrayList제네릭을 이용해 구현한 ArrayListprivate static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initial..