트랜잭션(Transaction)은 데이터베이스와 관련된 작업을 일관성 있게 처리하기 위한 개념입니다. 데이터베이스에서는 여러 개의 작업(예: 데이터 읽기, 쓰기, 업데이트)이 수행될 때 데이터의 일관성을 보장하기 위해 트랜잭션을 사용합니다. 트랜잭션은 "원자성(Atomicity)", "일관성(Consistency)", "격리성(Isolation)", "지속성(Durability)"을 나타내는 "ACID"라는 속성들을 갖습니다.
원자성 (Atomicity)
트랜잭션은 하나의 작업 단위로 간주되어, 모든 작업이 성공하면 커밋(Commit)되고, 하나라도 실패하면 롤백(Rollback)되어 이전 상태로 복구됩니다. 즉, 트랜잭션 내의 모든 작업은 원자적으로 실행됩니다.
일관성 (Consistency)
트랜잭션은 데이터베이스 상태를 일관된 상태로 유지합니다. 트랜잭션이 시작하기 전과 끝난 후에도 데이터베이스는 일관성을 유지해야 합니다.
격리성 (Isolation)
여러 개의 트랜잭션이 동시에 실행될 때, 각 트랜잭션은 다른 트랜잭션의 작업에 영향을 받지 않아야 합니다. 격리성은 동시성 문제를 해결하는 데 중요한 역할을 합니다.
지속성 (Durability)
트랜잭션이 성공적으로 커밋되면 그 결과는 영구적으로 저장되어야 합니다. 시스템이 다운되더라도 데이터는 손실되지 않아야 합니다.
트랜잭션은 일반적으로 다음과 같은 패턴으로 사용됩니다.
트랜잭션 시작
트랜잭션을 시작하고 데이터베이스 작업을 수행하기 전에 해당 트랜잭션을 시작합니다.
데이터베이스 작업
원하는 데이터베이스 작업(읽기, 쓰기, 업데이트)을 수행합니다.
트랜잭션 종료
모든 데이터베이스 작업이 성공하면 트랜잭션을 커밋하여 작업을 영구적으로 저장하고, 하나라도 실패하면 롤백하여 이전 상태로 복구합니다.
트랜잭션은 데이터베이스 시스템에서 중요한 개념으로, 데이터의 무결성과 일관성을 보장하는 데 사용됩니다. 이는 데이터베이스에서 복잡한 작업을 안전하게 수행하고, 동시성 문제를 관리하는 데 도움을 줍니다. Java에서는 Spring과 같은 프레임워크를 사용하여 트랜잭션을 쉽게 관리할 수 있습니다.
OS 공부할때 배운거 복습 겸으로 정리합니다.
동시성은 동시에 여러 작업 또는 스레드가 실행되는 상황을 가리킵니다. 동시성은 여러 사용자나 프로세스가 시스템을 동시에 사용하거나, 하나의 프로그램이 여러 작업을 병렬로 처리할 때 발생할 수 있습니다.
동시성 문제는 주로 다음과 같은 상황에서 발생합니다.
경쟁 조건 (Race Condition)
여러 스레드나 프로세스가 공유 데이터나 리소스에 접근하고 수정할 때, 어떤 스레드가 먼저 접근하여 데이터를 변경하면 다른 스레드가 그 변경을 덮어쓰는 상황이 발생할 수 있습니다. 이로 인해 데이터의 무결성이 깨지는 문제가 발생합니다.
데드락 (Deadlock)
두 개 이상의 프로세스나 스레드가 서로가 가진 리소스를 기다리면서 상호 대기하는 상황입니다. 각각의 프로세스나 스레드는 다른 리소스를 해제하지 않고 대기하므로 프로그램이 더 이상 진행되지 못하고 멈추게 됩니다.
스레드 간 통신 (Inter-Thread Communication)
여러 스레드가 작업을 나누어 수행할 때, 스레드 간에 정보를 안전하게 전달하고 동기화하는 문제가 발생할 수 있습니다. 이로 인해 잘못된 정보 전달이나 불일치가 발생할 수 있습니다.
@Transactional
어노테이션은 Spring에서 제공하는 기능 중 하나로, 메소드에 이 어노테이션을 붙이면 해당 메소드 내의 모든 데이터베이스 접근 작업이 하나의 트랜잭션으로 묶이게 됩니다. 이것은 데이터베이스 작업의 일관성을 보장하고, 트랜잭션 내에서 실행되는 모든 쿼리가 성공해야만 커밋(저장)되고, 하나라도 실패하면 롤백(취소)되는 것을 의미합니다.
예를 들어, 여러 개의 데이터베이스 작업을 하나의 트랜잭션으로 처리해야 할 때 유용합니다. 예를 들어, 한 메소드에서 여러 개의 업데이트나 삽입 작업을 수행하고, 이 작업들이 모두 성공해야만 데이터베이스에 반영하고 싶을 때 @Transactional
을 사용할 수 있습니다.
@Transactional
어노테이션을 메소드나 클래스에 붙이면 해당 메소드나 클래스 내의 모든 데이터베이스 작업이 하나의 트랜잭션으로 처리되므로, 데이터베이스 작업 간의 일관성을 유지하고 데이터베이스에 대한 안전한 업데이트 또는 롤백을 보장할 수 있습니다.
@Transactional
어노테이션은 다음과 같은 곳에 붙일 수 있습니다.
메소드에 직접 붙이기
특정 메소드 내의 모든 데이터베이스 작업을 하나의 트랜잭션으로 묶고 싶을 때 해당 메소드에 @Transactional
어노테이션을 붙입니다. 이 경우 해당 메소드 호출 시 트랜잭션이 시작되고, 메소드 수행이 완료되면 트랜잭션은 커밋 또는 롤백됩니다.
@Transactional
public void someMethod() {
// 여기서 데이터베이스 작업 수행
}
클래스에 붙이기
클래스 레벨에 @Transactional
어노테이션을 붙이면 클래스 내의 모든 메소드가 트랜잭션으로 처리됩니다. 이는 클래스 내에서 여러 메소드가 서로 관련된 데이터베이스 작업을 수행하고, 이 작업들을 하나의 트랜잭션으로 묶고자 할 때 유용합니다.
주로 Service단에서 Repository를 통해 DB에 접근하는 로직을 구현하면, 이 활용방법을 주로 사용합니다.
@Transactional
public class MyService {
public void method1() {
// 데이터베이스 작업
}
public void method2() {
// 데이터베이스 작업
}
}
스프링 구성 클래스 또는 XML 설정에서 붙이기
@Configuration
어노테이션이 붙은 스프링 구성 클래스나 XML 설정 파일에서 @EnableTransactionManagement
어노테이션을 사용하여 트랜잭션 관리를 활성화하고, 다음과 같이 특정 패키지 또는 클래스에 대한 트랜잭션 관리를 설정할 수 있습니다.
@Configuration
@EnableTransactionManagement
public class MyTransactionConfig {
// 트랜잭션 설정 및 패키지 레벨의 @Transactional 설정 가능
}
메소드 레벨에 커스텀 어노테이션으로 붙이기
커스텀 어노테이션을 정의하고, 이 어노테이션을 사용하여 메소드에 트랜잭션을 적용할 수 있습니다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
public @interface CustomTransactional {
}
// 사용 예시
@CustomTransactional
public void customTransactionMethod() {
// 데이터베이스 작업
}
일반적으로는 1번과 2번 방식이 가장 많이 사용됩니다. 메소드 레벨에 특정한 경우에만 트랜잭션을 적용하려면 1번 방식을, 클래스 내의 모든 메소드에 트랜잭션을 적용하려면 2번 방식을 사용하면 됩니다. 설정을 더 세밀하게 제어하려면 3번 방식을 사용할 수 있습니다.
동시성 이슈는 생각보다 무섭습니다.. 특히 예약 프로그램이나 배송 관련 구현이면 더더욱 아찔.. 헉 제일 아찔한건 송금 관련입니다..
개발을 하면서, OS에서 배운 동시성 이슈가 실제 상황에 적용되는 것을 간과했습니다. 초기에는 동시성 이슈에 대한 인식이 부족했습니다. 코드를 작성할 때, 여러 사용자가 동시에 접근하는 상황을 고려하지 않고 진행했었습니다. 이로 인해 가끔은 의도치 않은 버그와 데이터 불일치 문제가 발생하기도 했습니다.
저 같은 경우는 인터넷강의를 한번에 모아 놓은 게시판..? 을 만들고 있었는데 해당 이슈가 발생했습니다. (회사 일이라 자세히 말 못함)
동시성 문제를 경험하고 나서야, 이러한 문제를 해결하기 위해 @Transactional 어노테이션을 알게 되었습니다. (공부를 하면서 위와 같이 정리를 했습니다.)
@Transactional 어노테이션을 활용하면 여러 사용자가 동시에 데이터베이스에 접근할 때 발생할 수 있는 동시성 이슈를 효과적으로 관리할 수 있습니다. 이 어노테이션을 트랜잭션을 필요로 하는 메소드에 적용하면, 해당 메소드 내에서 모든 데이터베이스 작업이 하나의 트랜잭션으로 묶이게 됩니다.
@Service
@Transactional
public class MyService {
@Autowired
private MyRepository myRepository;
public void updateData() {
// 여러 데이터베이스 작업 수행
myRepository.updateRecord1();
myRepository.updateRecord2();
// ...
}
}
이렇게 하면 updateData()
메소드 내에서 모든 데이터베이스 작업이 하나의 트랜잭션으로 처리되어, 다른 사용자와의 충돌을 방지하고 데이터 일관성을 유지할 수 있습니다.
동시성 이슈는 소프트웨어 개발에서 피할 수 없는 문제 중 하나입니다. 그러나 이러한 문제에 대한 인식을 가지고, 적절한 도구와 기술을 활용하여 해결하는 과정은 개발자로서의 성장을 이끌어내는 중요한 경험입니다. @Transactional 어노테이션과 같은 도구를 적절히 활용하면 동시성 이슈를 효과적으로 관리할 수 있으며, 안정적인 소프트웨어를 개발하는데 도움이 됩니다.