Home 유연한 설계는 항상 필요한가?
Post
Cancel

유연한 설계는 항상 필요한가?

글을 작성하게 된 계기


맡은 프로젝트에서 불필요한 추상화, 최적화를 해서 복잡도를 올리다가 야근철야 까지 하게 되었습니다. 이 과정에서 든 생각을 정리하기 위해 글을 작성하게 되었습니다.





1. 왜 이런 생각이 들었을까?


프로젝트에서 불필요한 추상화와 최적화를 하다보니 구조가 복잡해졌고, 안그래도 빠듯한 일정에 야근에 철야까지 하게 되었습니다. 이 과정에서 유연한 설계에 대해 다시 한 번 생각해보게 되었습니다. 유연한 설계를 하면 변화에 잘 대응할 수 있어 유지보수가 쉽고, 새로운 요구사항도 쉽게 수용할 수 있죠. 그런데 곰곰히 생각해보니 입사 후, 유연한 설계가 필요한 경우가 정말 적었고, 내가 원하는 깔끔한 구조 를 만들고 싶어서 유연한 설계를 하려고 한게 아닌가? 라는 생각이 들었습니다.

심지어 제가 맡고 있는 서비스는 한 번 정해진 규칙이나 구조가 몇 년간 변하지 않는 것도 대부분이었습니다. 😐



그리고 일정이 딜레이되는데, 유연한 설계를 말하는게 맞나? 하는 생각도 함께 들었습니다. 최근 기술도 중요한데, 비즈니스는 더 중요하다는 생각이 들었기 때문에 생각이 많았거든요. 이런 고민 과정에서 필요할 때, 쉽게 변경할 수 있는 최소한의 장치가 있는 구조가 유연한 설계 아닐까? 라는 생각이 들었습니다. 미래는 예측할 수도 없고, 변화는 생각한 대로 오지도 않으니, 최대한 현재에 집중하고 변경을 위한 최소한의 여지만 남겨두는 것 이죠.

  • 일단 현재 요구사항을 충족하는 가장 심플한 구조를 만든다.
  • 현재 요구사항을 충족하는데 필요한 최소한의 유연성만 고려한다.
  • 예측 가능한 변화에 대해서만 유연하게 설계한다.







2. 어디까지 유연해야 할까?


그러면서 설계의 유연성에는 한계제약 사항 이 필요하다는 생각이 들었는데, 현재의 문제예상 가능한 변경 에 대해서만 유연하게 설계하는 것이 적당한 것 같았습니다. 유연해야 한다는 말을 그대로 따르다 보면, 끝없는 추상화복잡성의 늪 에 빠질 수 있기 때문입니다. 그렇다고 유연함을 완전히 배제하라는 의미는 아닌데요, 여기에 딱 맞는 원칙이 이미 있더라고요.

“You aren’t gonna need it” (YAGNI) is a principle which arose from extreme programming (XP) that states a programmer should not add functionality until deemed necessary.




구현체는 몇 년째 한 개인데, 불필요한 추상화를 하는 경우. 한 번쯤은 이런 경험 있지 않나요? 👀 이거 외에도 디자인 패턴을 과하게 사용한다던지, 너무 작은 단위로 인터페이스를 분리한다던지 제가 했던 수 많은 실수들이 떠오르더라고요.

1
2
3
4
5
interface PaymentService: PaymentProcessor {
    fun processPayment(command: PaymentCommand)
    
    ......
}
1
2
3
abstract class PaymentProcessor {
    ......
}





여튼, 비즈니스 요구사항에서 확실하게 반복될 수 있는 변화가 예측 된다면, 그 부분은 유연하게 설계할 이유가 충분합니다. 반면, “혹시 나중에 이런 경우도 생기지 않을까?” 하는 막연한 가능성에 대비 해 미리 확장 가능한 구조를 만드는 것은 위험합니다. 위에서 말했듯, 변화는 예측하지도 못할 뿐더러 설사 오더라도 내가 예측한 범위 밖에서 오는 경우가 많았거든요.

안타깝게도 변화가 왔을 때는 자잘한 변화보다 기획이나 설계 자체가 바뀌는 큰 변화가 훨씬 많았습니다.




요구사항이 잘 바뀌지 않는 시스템이라면, 굳이 복잡한 유연성을 고려할 필요는 더욱 적습니다. 이런 경우, 변경 가능성보다 현재의 명확성유지보수의 단순성 이 훨씬 더 큰 가치죠. 따라서 너무 많은 고민을 하기보다 요구사항을 빠르게 충족하며, 이 과정에서 예측 가능한 범위 까지만 대비를 하도록 합시다. 물론 그렇다고 아래처럼 하면 안되겠죠? 💩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Service
class PaymentService(
    private val paymentRepository: PaymentRepository,
    private val notificationService: NotificationService,
    private val userService: UserService
) {
    
    @Transactional
    fun processPayment(
        type: String?, 
        amount: BigDecimal?, 
        country: String?
    ) {
        if (type != null && amount != null) {
            if (type == "CARD") {
                if (country == "KR") {
                    ......
                } else if (country == "US") {
                    ......
                } else {
                    ......
                }
            } else if (type == "KAKAOPAY") {
                if (country == "KR") {
                    ......
                } else {
                    ......
                }
            } else if (type == "TOSS") {
                if (amount > BigDecimal("1000000")) {
                    ......
                } else {
                    ......
                }
            } else {
                ......
            }
        } else {
            ......
        }
    }
}







3. 정리


야근에 철야까지 하면서 유연한 설계에 대해 다시 한 번 생각해보게 되었습니다. 머리가 나쁘면 손발이 고생한다고, 몸으로 배우고 있네요. 조금씩 실무 경험이 생기면서 현실적 으로 변하고 있는 것 같은데, 참 생각이 많아집니다. 이상적으로 개발하는, 스스로 피곤할 정도로 고민하는 과정 이 나쁘지는 않았는데, 이젠 점점 현실과 타협하는 개발자 가 되는 것 같아서요. 처음 입사했을 때, 사수님들이 패기 넘친다고 했던 것 같은데 이제는 일단 일정 맞추고.... 가 먼저 나오네요.


This post is licensed under CC BY 4.0 by the author.

컴퓨터의 수 표현 방식과 Decimal을 사용하는 이유

레디스의 키 분배 전략:CRC16