본문 바로가기

전체 글

(58)
개발팀장의 얽힌 실타래 (소프트웨어 복잡성) 소프트웨어 복잡성개발을 시작한 지 2년이 넘어가고 개발 팀장이 되면서 소프트웨어 복잡성이라는 벽을 실감하기 시작했습니다. 작은 애플리케이션을 만들 때는 모든 게 단순해 보였지만, 시스템이 커져갈수록 보이지 않던 복잡성이 곳곳에서 드러났습니다.재미있게도 이 복잡성은 단순히 코드 구조나 아키텍처 문제만이 아니였습니다. 조직 구조, 커뮤니케이션, 일정 압박, 우선순위 결정 등 비기술적인 요소들과 복잡하게 얽혀 있다는 것입니다. 한가지 예를 들어보겠습니다. 어떤 서비스가 Web 서비스와 API 서비스를 모두 제공한다고 가정할 때, 사용자의 요청으로 ‘A’라는 기능을 개발하게 되었습니다. 처음엔 Web 서비스에만 우선적으로 기능을 추가하고, API 서비스는 나중에 개발하기로 계획했습니다. 그러나 시간이 흐르면서 ..
[JPA] fetchJoin 과 Paging 처리의 한계 및 해결 방안 EntityGraph를 사용하여 fetch join을 할 경우, 쿼리에서 직접적으로 페이징 처리가 어려워집니다. 이유는 다음과 같습니다: 1. 복잡한 조인 구조예를 들어, A, B, C 세 테이블을 조인하는 경우를 생각해 봅시다. A와 B는 1:N 관계이고, B와 C도 1:N 관계라면, 예를 들어 N이 3이라 가정할 때페이지 크기가 10이면, 실제로 10개의 A 엔티티를 가져오더라도 조인 결과로는 최대 10 × 3 × 3 = 90개의 행(row)이 반환될 수 있습니다. 2. RDBMS와 객체지향의 차이관계형 데이터베이스에서는 위와 같이 다중 조인된 결과에 대해 LIMIT을 적용할 때, 원래의 A 엔티티 기준으로 제한하지 않고 전체 행에 대해 제한을 걸게 됩니다.그래서 결과적으로 full scan을 하고,..
실전 문제와 해결 전략 이번 글에서는 백엔드 실전 문제들을 살펴보고, 각각의 문제를 해결하는 방법에 대해 정리해보겠습니다.1. CS - CAS(Compare-and-Swap) 활용  문제: synchronized 없이 다중 스레드 환경에서 동시성 제어하기 멀티스레드 환경에서 하나의 변수를 여러 스레드가 동시에 수정하는 경우 경합 상태(Race Condition)가 발생할 수 있습니다. 기존에는 synchronized 키워드를 사용하여 동기화했지만, 이는 성능 저하의 원인이 될 수 있습니다. 이를 해결하기 위해 CAS(Compare-and-Swap) 연산을 활용한 AtomicInteger 사용을 고려해야 합니다. 해결 방법: AtomicInteger를 활용한 CAS 동기화import java.util.concurrent.atom..
Socket 통신 1. Socket 통신이란 무엇인가?Socket은 네트워크 프로그래밍에서 가장 기본적이고 중요한 개념 중 하나입니다. 다양한 네트워크 애플리케이션(채팅, 실시간 게임, 파일 전송 등)을 개발하려면 클라이언트-서버 간 통신이 필수적입니다. 이때 Socket은 데이터를 주고받는 양 끝단의 연결점을 의미합니다. 2. Socket 통신의 기본 개념2.1. Socket이란?Socket은 네트워크 상의 두 호스트 간 통신을 위한 인터페이스입니다.주로 TCP/IP 또는 UDP 프로토콜 위에서 데이터를 주고받을 때 사용됩니다. 2.2. TCP와 UDP의 차이구분TCPUDP프로토콜 타입연결형(Connection-oriented)비연결형(Connectionless)신뢰성데이터 무결성 및 순서 보장신뢰성 보장 안 함속도느림..
코어 수에 따른 프로세스와 쓰레드 개수의 관계와 성능 영향 코어 수와 쓰레드 수의 관계는 멀티스레드 프로그래밍에서 성능 최적화의 핵심 요소입니다. 쓰레드 수가 특정 코어 개수와 맞지 않을 때 성능이 저하되는 이유는 여러 가지가 있으며, 이는 CPU가 수행하는 컨텍스트 스위칭, 메모리 대역폭 문제, 그리고 CPU 코어의 오버헤드와 관련이 있습니다. 이를 구체적인 예시로 살펴보겠습니다. 1. 코어 개수와 쓰레드 개수의 관계CPU는 보유한 코어 수만큼 작업을 동시에(병렬성) 수행할 수 있습니다. 예를 들어, 4개의 코어가 있는 CPU는 이론적으로 최대 4개의 쓰레드를 동시에 실행할 수 있습니다. 이보다 많은 쓰레드가 실행될 경우, 운영 체제는 TCB(Thread Control Block)을 사용해 각 쓰레드의 상태를 저장하고 복구하면서 컨텍스트 스위칭을 수행하게 됩니..
대규모 트래픽에 대비한 아키텍처 확장 전략 이 글에서는 기본적인 Application 서비스 환경에서 대규모 서비스로의 전환을 시간 순으로 나열해보겠습니다. 현재  시스템 구성현재 서비스는 1개의 Nginx 웹 서버와 2개의 Tomcat 애플리케이션 서버로 구성되어 있습니다.기존요청의 흐름사용자는 로컬 또는 DNS 서버를 통해 IP를 받아와 HTTPS(443 포트)로 요청을 보냅니다.요청은 웹 서버를 거쳐 애플리케이션 서버로 전달되고, 필요한 경우 데이터베이스(DB)에서 데이터를 가져와 사용자에게 응답을 전송합니다.HTTP 요청과 응답 구조를 따릅니다.대규모 서비스 전환 단계이제 각 컴포넌트의 성능을 높이고 트래픽을 효과적으로 분산하기 위해 시스템을 확장하는 단계를 살펴보겠습니다. 1. Database 의 Scale Up현재 DB 성능이 충분하지..
[중요!] @Transactional(AOP)가 동작하지 않는 이유 this.update(); 이전부터 작성하고 싶었던 주제였습니다. @Transactional 같은 AOP를 사용할 때 종종 발생하는 문제 중 하나는"어노테이션을 붙였는데 값이 업데이트되지 않지? AOP가 작동하지 않지?" 같은 상황입니다.코드상으로도 문제없어 보이고 에러도 나지 않지만, 기능이 제대로 작동하지 않을 때가 있죠. 다음 예제 코드에서 그 문제를 확인해 보겠습니다.updateUserWithLogging() 메서드를 호출하면 로그는 정상적으로 출력되지만,user 객체의 업데이트가 이루어지지 않는 문제가 있습니다. 여기서 @Transactional이 적용되어 있으므로 TransactionManager가 실행되어야 합니다. 그러나 UserController에서 updateUserWithLogging() 메서드를 호출할 경우 ..
스프링 의존성 주입(DI) Spring 프레임워크의 핵심 기능 중 하나는 의존성 주입(DI, Dependency Injection)입니다. DI는 객체 간의 결합을 줄이고 코드의 유연성과 확장성을 높이는 설계 방식으로, 스프링에서는 이를 통해 객체 간의 관계를 간단하게 설정할 수 있습니다. 1. 의존성 주입(DI)란?의존성 주입(DI)이란 객체가 사용할 의존성을 외부에서 주입해주는 설계 패턴입니다. 전통적인 객체 지향 프로그래밍에서는 객체 내부에서 필요한 의존성을 직접 생성하거나 설정합니다. 하지만 DI를 사용하면 외부에서 필요한 의존성을 전달받아 객체가 스스로 관리할 필요 없이 주어진 의존성만 사용할 수 있게 됩니다. 예시public class OrderService { private final PaymentService ..