반응형

 

Spring에서 DB에 대한 연산을 할 때 

@Transactional 어노테이션을 통해 트랜젝션 작업 단위로 묶어서 처리한다.

 

트랜젝션을 위해 @Transactional 어노테이션을 붙여도 동작하지 않는 상황에 주의해야한다.

 

1. @Transactional을 붙인 메서드의 접근지정자가 private인 경우

public class GameService {
	
	//..........
    
	public void doSave() {
		save();
	}
    
	@Transactional
	private void save() {
		//..........
	}
	
}

이는 Spring AOP가 프록시 기반으로 하기 때문에 발생하는 것으로 접근지정자를 public으로 지정하면 된다.

 

2.  @Transactional이 붙지 않은 메서드에서 @Transactional이 붙은 메서드를 호출하는 경우

public class GameService {
	
	//..........
    
	public void doSave() {
		save();
	}
    
	@Transactional
	public void save() {
		//..........
	}
	
}

@Transactional은 Proxy를 이용해 메서드가 실행되기 전 트랜젝션을 묶게 된다. 따라서 @Transactional이 붙지 않은 doSave() 메서드를 호출하게되면 트랜젝션이 적용되지 않은 상태로 doSave() 메서드를 실행하고 이 메서드 내에서 save() 메서드를 호출하게 되므로 save() 메서드에 트랜젝션이 정상적으로 적용되지 않는다.

 

해결방법

- doSave를 통해 save를 호출하는게 아닌 save 메서드를 호출하게 되면 정상적으로 트랜젝션이 동작하게 된다.

- doSave메서드에 @Transactional 어노테이션을 붙인다.

public class GameService {
	
	//..........
    
	@Transactional
	public void doSave() {
		save();
	}
    
	@Transactional
	public void save() {
		//..........
	}
	
}

 

반응형

'공부 > Spring' 카테고리의 다른 글

[Spring] DI란?  (0) 2021.09.20
[Spring] IoC란 무엇인가?  (0) 2021.09.16
[Spring] AOP란?  (0) 2021.09.08
[Spring] Filter & Interceptor  (0) 2021.05.05
Servlet Working Flow  (0) 2021.05.04
반응형

싱글톤 패턴은 최초 한번만 메모리를 할당하고 객체를 만들어 사용하는 디자인 패턴으로 생성자를 반복호출해도 최초 생성된 객체를 반환해준다.

 

장점

- 객체를 한개만 만들어서 메모리 낭비가 적다

- 싱글톤 객체와 다른 객체들 간의 데이터 공유가 쉽다

 

단점

- 싱글톤 클래스의 역할이 커지면 결합도가 높아져 객체 지향 원칙에 어긋날 수 있다.

- 멀티쓰레드 환경에서 중복 초기화가 발생할 수 있다.

 

싱글톤을 구현하는 여러 방법

static 활용

public class Singleton {
 
    private static Singleton instance = new Singleton();
    
    private Singleton(){}
    
    public static Singleton getInstance(){
    
        return instance;
    }
}

위 방법은 애플리케이션 실행시 정적으로 객체 생성

이 방법의 경우 실행시 바로 객체를 생성하는 방법으로 객체의 크기가 커지면 부담이 될 수 있다.

class 로드전 정적 생성으로 Thred-Safe하다.

 

지연 생성 방법

public class Singleton {
 
    private static Singleton instance;
    
    private Singleton(){}
    
    public static Singleton getInstance(){
    
        if(instance == null){
        
            instance = new Singleton();
        }
        
        return instance;
    }
}

위 방법은 멀티 스레드 환경에서 예기치 못한 문제를 만들 수 있다. (두 스레드가 getInstance를 동시에 한 경우)

 

synchronized 사용 방법

public class Singleton {
 
    private static Singleton instance;
    
    private Singleton(){}
    
    public static synchronized Singleton getInstance(){
    
        if(instance == null){
        
            instance = new Singleton();
        }
        
        return instance;
    }
}

synchronized를 사용하면 멀티 스레드 환경에서 Thred-safe할 수 있다.

다만 synchronized에 의한 성능저하가 발생할 수 있다.

 

Inner Class 활용

public class Singleton {
	
    private Singleton(){}
    
    private static class SingletonHelper{
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
}

해당 방법은 내부 클래스를 활용한 방법이다.

getInstance를 호출하기 전까지 Singleton 객체가 생성되지 않으며, getInstance호출 시 정적 객체 생성

객체가 바로 생성되지 않는 지연 생성 방법이며 정적 인스턴스로 syncronized를 사용하지 안하 성능 저하도 없다.

 

 

 

반응형
반응형

Spring의 핵심 요소

- 제어 역전(IoC, Inversion of Control)

- 관점 지향 프로그래밍(AOP, Aspect Oriented Programming)

- 서비스 추상화(PSA, Portable Service Abstraction)

라고 볼 수 있다.

 

오늘은 그 중에서 AOP에 대해서 정리해보려고 한다.

 

AOP란 무엇일까?AOPAspect Oriented Programming, 관점 지향 프로그래밍은 절차지향 프로그래밍과 같이하나의 패러다임이라고 볼 수 있다.

 

AOP는 좀 더 비즈니스 로직에 집중하는 것을 말한다.

가령 우리가 어떤 물품을 판매하는 사이트를 만들었다고 가정하면,

그 안에는 여러 기능들이 존재할 것이고 그 기능들은 데이터베이스를 통해 데이터를 가져오거나 저장할 것이다.

이러한 경우, 데이터베이스에 데이터를 쓰거나 읽을 때 우리는 트랜젝션을 통해 논리적 작업단위로 묶어서 데이터를 처리한다.

 

그렇다면 각 기능에는 트랜젝션과 비즈니스 로직이 함께 들어가야할 것이다.

 

비즈니스 로직을 개발할 때 공통의 중복된 기능을 주입하여 비즈니스 로직에만 더 집중해서 개발하고자 하는게 관점 지향적 프로그래밍의 핵심이라고 볼 수 있다.

즉, 내가 필요한 기능이 있다면 기능을 주입해서 사용할 수 있는 것이다.

 

가장 좋은 예시가 Spring의 @Transactional이다.

 

우리가 Spring을 통해 개발할 때 메서드나 Class 위에 @Transactional 어노테이션을 사용한다.

이런 경우 우리가 작성한 데이터 처리 메서드가 시작될 때 그리고 종료될 때에 맞춰서 트랜젝션이 시작되고 종료된다.

 

우리에게 필요한 공통 기능을 만들고 필요할 때 주입해서 사용함으로서 좀 더 비즈니스 로직에 집중할 수 있도록 하는게 AOP이다.

 

추가로 Spring의 AOP는 프록시(다이나믹 프록시)를 통해 AOP를 구현하고 있다.

우리가 어떤 메서드를 호출하게 되면 이를 프록시가 대신 실행해주면서 추가적인 기능을 실행해주게 된다.

A 메서드 실행하고자 할 때 이를 프록시에게 맡겨서

프록시 -> 추가기능 실행 -> A 메서드 실행 이런식으로 동작하게 된다.

 

다음에는 Spring의 AOP동작에 대해서 작성해보겠다.

 

 

 

틀린점이나 추가해야할 것이 있다면 알려주세요.

 

반응형

'공부 > Spring' 카테고리의 다른 글

[Spring] DI란?  (0) 2021.09.20
[Spring] IoC란 무엇인가?  (0) 2021.09.16
@Transactional 동작하지 않는 경우  (0) 2021.09.10
[Spring] Filter & Interceptor  (0) 2021.05.05
Servlet Working Flow  (0) 2021.05.04
반응형

Interface Segregation Principle은 하나의 거대한 Interface 를 사용하기 보다는 여러개의 작은 Interface를 사용하는 원칙이다.

Interface Segregation Principle을 지키지 않은 코드

public interface Animal {
	void eat();
  void fly();
}

public class Cat implements Animal {
	@Override
	public void eat() {
		System.out.println("Eat Cat food");
	}

	@Override
	public void fly() {
		throw new UnsupportedOperationException("I can not fly!");
	}
}

Animal은 하늘과 육지 동물들을 모두 포함하는 인터페이스이다.

그러다 보니 eat, fly 메소드가 존재한다.

하지만 fly는 육지 동물에게는 필요 없을 수 도 있다.

Animal을 구현하고 있는 Cat은 날 수 없다. 따라서 fly 메소드는 Cat에는 필요가 없다. 하지만 Animal을 구현해야하기 때문에 구현할 필요가 있다.

이는 Interface Segregation Principle이 준수되지 않은 경우 이다.

Interface Segregation Principle을 준수하는 코드

public interface Eater {
	void eat();
}

public interface Flyer {
	void fly();
}

public class Cat implements Eater {
	@Override
	public void eat() {
		System.out.println("Eat Cat food");
	}
}

위 예제 코드를 보면 하나의 Animal 인터페이스에서 Eater, Flyer와 같은 작은 인터페이스로 분리되어 있고, Cat은 필요한 인터페이스만 구현하고 있다.

이렇게 되면 불필요한 메소드를 구현할 필요가 없어지게 된다.

즉, 필요한 의존성만을 가지게 된다.

반응형
반응형

카프카의 특징

  • 디스크에 데이터 적재
  • 수평적으로 확장 가능
  • 대량의 데이터를 빠르게 처리
  • 실시간 데이터 스트리밍을 지원
  • 파티션 내 메시지 순서 보장

카프카는 메시지 큐로서 시스템 간 비동기적 데이터 처리를 위해 많이 사용이 된다

ex) 메시지 알림 센터, 배달 주문 접수 등

반응형

'공부 > Kafka' 카테고리의 다른 글

[Kafka] Zookeeper와 Broker  (0) 2025.06.29
[Kafka: 카프카] Kafka란?  (0) 2021.06.07
반응형

Kafka는 pub-sub 구조를 가지는 큐이다.

 

Kafka는 프로듀서, 브로커, 컨슈머 이렇게 3개로 이루어져 있고,

프로듀서에서 메시지를 브로커에게 전달하면 전달된 메시지를 컨슈머가 브로커를 통해 가져와 처리하게 된다.

 

Kafka의 처리 흐름을 보면

 

 

 

프로듀서 -> 브로커 <- 컨슈머

로 브로커를 중심으로 프로듀서가 메시지를 브로커에 전달하고, 컨슈머가 브로커에게 가서 메시지를 받아와 처리하는 구조이다.

 

Kafka는 많은 장점을 가지고 있다.

그 중 두드러 지는 장점이 고성능, 고가용성, 확장성 이다.

Kafka는 일반적인 MQ 시스템에서 지원하는 기능들을 배제함으로서 message당 오버헤드를 줄이는 등을 통해 고성능을 확보했다고 한다.

 

 

Kafka는 정해진 시간 동안 브로커가 받은 메시지를 가지고 있기 때문에 컨슈머가 이슈로 정상 동작을 하지 않을 경우 다른 컨슈머가 브로커에 저장된 메시지를 가지고 이어서 작업할 수 있어 가용성이 높다.

 

그리고 Kafka의 브로커는 topic과 partition으로 이루어져있다. 하나의 토픽에 여러 파티션이 있을 수 있고 메시지는 파티션에 저장되게 된다. 브로커가 프로듀서로부터 많은 메시지를 받는 경우 필요시 파티션을 늘려 수신되는 메시지를 분산 시킬 수 있다. 이때 기본 설정은 RR로 동작하게 된다. 이렇게 늘린 파티션에 컨슈머를 추가로 배치하여 수평 확장을 할 수 있다.

 

 

Reference:

Kafka: a Distributed Messaging System for Log Processing (논문)

반응형

'공부 > Kafka' 카테고리의 다른 글

[Kafka] Zookeeper와 Broker  (0) 2025.06.29
[Kafka] Kafka의 특징  (0) 2021.06.13
반응형

Liskov Substitution Principle은 서브타입이 부모타입으로 대체될 수 있어야 한다는 것이다.

이렇게 보면 이해하기가 조금 난해할 수 있다.

간단히 설명하면 상속 또는 인터페이스를 구현하는 어떤 클래스가 상위 클래스에 대체될 수 있어야하고, 그 때 일관된 동작을 보장해야한다는 것이다.

즉, 일관성과 안정성을 보장해야한다는 원칙이다.

Liskov Substitution Principle을 준수하지 않는 코드

public class Bird {
	
	public void fly() {
		System.out.println("fly high!");
	}
}

public class Penguin extends Bird {
	
	@Override
	public void fly() {
		throw new IllegalStateException("I can not fly!");
	}
}

public class Zoo {
	public static void main(String[] args) {
		
		System.out.println("새들이 나는 공연");
		
		List<Bird> birds = new ArrayList<>();
		birds.add(new Bird());
		birds.add(new Penguin());
		
		birds.forEach(Bird::fly);
	}
}

Liskov Substitution 원칙을 준수 하지 않은 코드를 보면 Bird를 보면 해당 클래스는 fly라는 메소드가 있고 하늘을 나는 기능을 수행하는 메소드임을 알 수 있다.

그러나 Bird를 상속하는 자식 클래스인 Penguin은 Bird를 상속하고 있어 fly 메소드가 있어 하늘을 나는 기능을 수행할 것이라고 예상되나 Penguin은 하늘을 날 수 없다.

이러한 경우 Penguin은 부모 클래스인 Bird를 상속 하였지만 Bird클래스를 대체하여 일관적인 기능(fly)를 제공할 수 없다.

Bird를 사용하는 부분이 Penguin으로 대체 되어도 정상 동작 되어야하나 Penguin은 날지 못하기 때문에 이러한 기능을 제공할 수 없다

Liskov Substitution Principle을 준수하는 코드

public interface Flyable {
	void fly();
}

public class Bird {
	public void sing() {
		System.out.println("Singing");
	}
}

public class Magpie extends Bird implements Flyable {
	@Override
	public void fly() {
		System.out.println("I can fly!");
	}
}

public class Penguin extends Bird {
	
}

public class Zoo {
	public static void main(String[] args) {
		System.out.println("새들이 나는 공연");
		
		List<Bird> allBirds = List.of(new Magpie(), new Penguin());

    System.out.println("새들 노래");
    allBirds.forEach(Bird::sing);

    System.out.println("날 수 있는 새들 공연");
    allBirds.stream()
        .filter(b -> b instanceof Flyable)
        .map(b -> (Flyable) b)
        .forEach(Flyable::fly);
  }
}

Flyable 인터페이스로 fly기능을 분리하고 fly가 필요한 경우 해당 기능을 구현하도록 함으로서 Liskov Substitution Principle을 준수하였다고 볼 수 있다.

 

리스코프 치환 원칙에서 중요한 부분은 일관된 동작을 보장해야 한다는 부분이라고 생각한다.

반응형
반응형


도커 공부 내용 정리

도커는 프로그램을 가상환경에서 구동할 수있게 해주는 유용한 도구 입니다.
일반적으로 가상환경이라고 하면 VM을 떠올릴 수 있는데 도커는 이와 비슷하지만 조금 다르다.

아래 그림이 도커를 이해하는데 조금은 도움이 될 수 있다.


도커 컨테이너 와 가상머신


완쪽은 도커 오른쪽은 VM을 보여준다.
가장 큰 차이로는 Guest OS의 여부로 볼 수 있다. VM의 경우 한 VM당 하나의 Guest OS를 가지고 있으며 이에 따라 많은 오버헤드를 갖게 된다.
그러나 도커의 경우 Host OS 위에 도커 엔진이 올라가고 이 도커 앤진을 통해 필요한 컨테이너를 실행할 수 있다.

도커의 경우 라눅스에 있는 namespaces와 cgroups을 활용해 만들어진 기능이다. namespaces는 프로세스나 파일시스템 등을 namespace라는 것을 통해 논리적으로 구분하는 것이고 cgroups는 시스템의 자원을 격리하여 나누는 기술이다.

이 두 기술이 도커를 있게 해준 기반 기술이다.


반응형

+ Recent posts