본문 바로가기
기초 개념

개발 시 사이드 이펙트(Side Effect)란?

by 시니성 2023. 8. 24.

사이드 이펙트는 프로그래밍에서 어떤 함수나 연산의 수행 결과로 시스템의 상태가 예상치 못하게 변경되는 현상을 말합니다. 순수 함수(pure function)는 같은 입력 값에 대해 항상 동일한 결과를 반환하고 사이드 이펙트를 일으키지 않습니다. 그러나 많은 함수는 내부 상태나 외부의 시스템 상태를 변경하는 동작을 포함하며, 이런 동작이 종종 사이드 이펙트를 초래합니다.

사이드 이펙트 예시:

var globalVar = 0

fun addWithSideEffect(x: Int, y: Int): Int {
    globalVar += x
    return x + y
}

위의 addWithSideEffect 함수는 두 숫자를 더하는 것 외에 전역 변수 globalVar의 값을 변경하는 사이드 이펙트를 갖습니다.

Spring MVC에서의 사이드 이펙트 예시:

Spring MVC는 웹 애플리케이션을 구축하기 위한 프레임워크입니다. 이러한 웹 애플리케이션은 다양한 요청과 응답, 데이터베이스 연결 및 여러 외부 시스템과의 통신 등 복잡한 작업을 처리해야 하기 때문에 사이드 이펙트가 발생할 가능성이 큽니다.

예시 1: 데이터베이스 변경

Controller나 Service 내의 메서드가 데이터베이스에 직접 변경을 가하는 경우, 해당 변경은 사이드 이펙트로 볼 수 있습니다.

@RestController
@RequestMapping("/users")
class UserController(val userRepository: UserRepository) {

    @PostMapping("/create")
    fun createUser(@RequestBody user: User): ResponseEntity<User> {
        // 데이터베이스에 유저를 저장하는 동작 (사이드 이펙트 발생)
        val savedUser = userRepository.save(user)
        return ResponseEntity.ok(savedUser)
    }
}

예시 2: 전역 상태 변경

Spring의 싱글턴 빈은 애플리케이션의 생명주기 동안 하나의 인스턴스만을 가집니다. 이러한 빈의 상태를 변경하면 다른 부분에서도 영향을 받을 수 있습니다.

@Service
class CounterService {
    private var counter = 0

    fun increment(): Int {
        // 전역 상태 변경 (사이드 이펙트 발생)
        return ++counter
    }
}

위의 CounterService는 내부 카운터 값을 변경하는 increment 메서드를 가지고 있습니다. 이 메서드는 상태를 변경하는 사이드 이펙트를 갖습니다.


사이드 이펙트 예시 수정:

원래의 함수에서는 전역 변수 globalVar에 값을 추가하는 동작이 있었습니다. 이를 제거하고 순수한 덧셈 기능만을 수행하도록 변경합니다.

// 원래의 함수
var globalVar = 0

fun addWithSideEffect(x: Int, y: Int): Int {
    globalVar += x
    return x + y
}

// 수정된 함수
fun addWithoutSideEffect(x: Int, y: Int): Int {
    return x + y
}

Spring MVC에서의 사이드 이펙트 예시 수정:

예시 1: 데이터베이스 변경

사이드 이펙트 자체를 완전히 제거하는 것은 어렵습니다. 왜냐하면 데이터베이스에 데이터를 저장하는 동작 자체가 사이드 이펙트를 가지고 있기 때문입니다. 그러나 우리는 코드의 명확성을 높여 사이드 이펙트가 예상 가능하도록 만들 수 있습니다.

  1. Repository Layer
@Repository
interface UserRepository : JpaRepository<User, Long> {}
  1. Service Layer 생성
@Service
class UserService(private val userRepository: UserRepository) {

    fun createUser(user: User): User {
        return userRepository.save(user)
    }
}
  1. Controller Layer 수정
@RestController
@RequestMapping("/users")
class UserController(private val userService: UserService) {

    @PostMapping("/create")
    fun createUser(@RequestBody user: User): ResponseEntity<User> {
        val savedUser = userService.createUser(user)
        return ResponseEntity.ok(savedUser)
    }
}

이렇게 수정하면, 컨트롤러는 서비스 레이어를 통해 데이터베이스 작업을 수행하게 되므로, 각 계층의 책임이 분명해집니다. 여기서 UserService는 실제 데이터베이스 작업을 수행하기 위해 UserRepository에 의존하고 있습니다.

결론

사이드 이펙트는 프로그램에서 피할 수 없는 부분이며, 때로는 의도적으로 사용됩니다. 그러나 예기치 않은 사이드 이펙트는 버그의 원인이 될 수 있으므로, 코드를 작성할 때는 가능한 순수한 함수를 사용하고, 사이드 이펙트를 최소화하는 것이 좋습니다.

'기초 개념' 카테고리의 다른 글

제네릭<Generic>  (0) 2023.11.22
`withCredentials`에 대한 설명  (0) 2023.09.12
Eval 함수와 보안 문제  (0) 2023.08.29