어떤 자원을 지정할 때는 숫자로 된 id를 사용하곤 합니다. 만약 id가 엄청 커진다면 어떤 자료형을 사용하는 것이 적절할까요 ?
Integer로 약 -21억 ~ +21억까지 표현이 가능한데요. id 값은 일반적으로 양의 정수만 사용하므로 약 21억개의 id를 생성할 수 있습니다.
그런데 서비스를 만들다 보면 서비스의 규모에 따라 한 종류의 자원이 21억 개를 넘어서는 상황이 충분히 생길 수 있습니다. 따라서 훨씬 더 큰 자료형인 Long을 사용하거나 Long으로도 표현하기에 큰 수라면 UUID를 사용해도 좋을 것 같습니다.
통상적인 애플리케이션은 Long으로 표현할 수 있는 범위 내의 id 값 정도만 사용하므로 표현 범위의 문제라면 Long을 사용해도 충분하다.
하지만 1씩 증가해야하는 id발급에 성능상의 문제가 있다면 UUID 같이 랜덤하게 생성되는 id를 사용하는 편이 더 좋다.
레이어드 아키텍처에 대해 설명해 보세요 .
레이어드 아키텍처는 애플리케이션을 계층별로 나눠 각 계층에 적절한 책임과 역할을 부여해 계층별 재사용성을 높이는 설계 방법입니다. 과거에는 Controller- Service - DAO 형태의 3개 계층으로 나눈 3-티어 아키텍쳐가 유행했고 현재는 표현, 응용 도메인, 인프라스트럭처로 나눈 4-티어 아키텍처가 널리 사용되고 있는 것으로 알고 있습니다.
이렇게 티어를 나누어 사용하면 마치 디자인 패턴처럼 동일한 구조의 아키텍처에 익숙한 사람이 좀 더 빠르게 프로젝트 구조를 이해하는 데 도움을 줄 수 있습니다.
스레드 세이프하다(Thread Safety)는 말은 어떤 의미인지 설명해 보세요.
스레드 세이프하다는 것은 주로 어떤 코드나 라이브러리가 멀티 스레드 환경에서 적절한 동기화를 처리해 정상적인 동작을 보장하는지를 의미합니다.
만약 스레드 세이프하지 않는다면 레이스 컨디션 같은 문제가 발생하고, 데이터 불일치할 위험이 있습니다.
컬렉션 중 스레드 세이프한 자료형인 Vector와 스레드 세이프하지 않은 자료형인 ArrayList가 대표적 입니다.
그럼 항상 스레드 세이프한 컬렉션을 사용해야 하는가하는 문제가 있는데요
그것은 해당 컬렉션 인스턴스에 동시에 여러 개의 스레드가 접근할 수 있는지에 따라 달라져야 합니다.
동기화라는 것은 결국 락(lock) 같은 동기화 매커니즘을 사용하여 성능을 희생하게 되는데, 동기화가 필요하지 않은 곳에 스레드 세이프한 자료형을 사용하면 성능에 손해를 보게 되기 때문입니다.
ArrayList가 사용되는 환경은 보통 단일 스레드가 접근하여 컬렉션을 다루는 상황이 많기 때문에 스레드 세이프하지 않아도 문제가 되지 않은 경우가 많습니다.
레이스 컨디션:
여러 프로세스나 스레드가 공유 자원에 동시에 접근할 때 발생하는 보안 취약점
타이밍이나 실행 순서에 따라 예상치 못한 결과가 발생하는 현상
애플리케이션을 개발할 때 DTO를 만들어 사용하는 이유가 뭔가요 ?
DTO는 계층 간 데이터를 전달하기 위해 사용되는 객체입니다. DTO가 없다면 도메인 객체가 그대로 표현 계층까지 와서 클라이언트에게 그대로 노출될 겁니다.
그런 상황에서 도메인 객체의 필드나 구조를 변경하는 것이 클라이언트에게 노출됩니다.
그럼 해당 API를 바라보고 있는 클라이언트가 바로 영향을 받게 되지만, DTO를 사용하면 이런 상황을 완화시킬 수 있습니다.
중간에 변환 로직이 있기 때문에 내부적으로 도메인 객체가 변경되어도 DTO 변환 로직만 잘 변경해 주면 변겨으이 여파가 클라이언트에게 미치지 않도록 만들 수 있습니다.
또한 DTO는 하나의 도메인 객체에 목적별로 여러 개의 DTO를 두어서 서로 다른 API에 대해 서로 다른 DTO를 제공하는 것도 가능하게 만들 수 있습니다.
DTO를 사용하지 않는 경우:
// 도메인 객체가 그대로 노출
public class Product {
private Long id;
private String name;
private int price;
private String internalCode; // 내부용 코드
private boolean isDeleted; // 논리적 삭제 플래그
private LocalDateTime createdAt; // 생성 시간
private String createdBy; // 생성자
}
@GetMapping("/products/{id}")
public Product getProduct(@PathVariable Long id) { // 도메인 객체 직접 반환
return productService.findById(id);
}
문제점:
내부 구현이 그대로 노출됨
불필요한 데이터까지 전송됨
도메인 객체 변경이 API 응답에 직접 영향
스프링 프레임워크에서는 왜 빈을 만들고 주입하여 사용할까요 ?
빈은 스프링 프레임워크에서 관리하는 인스턴스들을 이야기합니다.
스프링 프레임워크는 애플리케이션 시작 시 특정 에너테이션들이 붙어 있는 클래스들을 빈으로 등록하고, 의존성이 필요한 곳에 해당 빈을 주입하여 사용합니다.
이렇게 하면 동일한 인스턴스는 한 번만 생성되고 여기 저기서 재사용되기 때문에 인스턴스를 만드는 비용과 메모리를 아낄 수 있습니다. 따라서 이것은 성능상의 이점이 됩니다.
또 다른 이점으로는 의존성을 주입받아서 사용하는 것에는 객체지향적인 이점이 발생합니다.
애플리케이션을 개발하다 보면 테스트코드를 작성할 때 특정 의존성의 대역(stub) 의존성을 주입받아서 사용하고 싶은 경우가 생깁니다. 이런 상황에서 의존성을 주입받아서 사용하는 경우 원래 코드를 수정하지 않고도 아주 손쉽게 테스트할 때 의존성을 주입받아서 사용하고, 실제 애플리케이션 실행되는 환경에서 실제 동작하는 의존성을 주입받아서 사용할 수 있습니다.
컨트롤러 코드에 @Controller라는 애너테이션을 붙인 컨트롤러와 @RestController라는 애너테이션을 붙인 컨트롤러가 있습니다. 두 가지 애너테이션은 뭐가 다른가요 ?
@Controller 애너테이션은 MVC패턴에서 사용되고 각 @RequestMapping들의 반환 값이 JSON이 아닌 뷰의 경로가 됩니다.
즉 클라이언트는 뷰를 통해 렌더링된 HTML페이지를 보게 됩니다.
반면, @RESTController는 각각의 @RequestMapping에 @ResponseBody 애너테이션이 붙어 있는 것과 동일합니다.
즉, 뷰가 아니라 JSON을 응답으로 반환합니다.