확장성을 이야기하는 이는 많습니다. 그러나 확장성의 의미를 고민하고, 이를 위하여 노력하는 엔지니어는 많지 않습니다. 확장성이라는 표현은 현재보다 미래에 연관되어 있기에, 모호하고 주관적일 수밖에 없습니다. 그럼에도 명확한 것은, 확장성에 대한 고민의 흔적이 반드시 코드에 남는다는 것입니다. 피플펀드컴퍼니에서 엔지니어로 일하며 고민하고 있는, 확장성에 대한 이야기입니다.

가짜 확장성: 혹시나

“혹시나”는 확장성이 아닙니다. 자신의 문제 해결 방안에 확신이 없거나 더 생각하기 귀찮을 때면, “혹시나 발생할지 모르는 예외를 대비해서 이런 것도 고려해두면 좋지 않을까?” 하는 생각이 들곤 합니다. 그러나 알아내기 어려운 예외는 있어도, 알 수 없는 예외는 없습니다. 예외 처리는 코드와 DB를 확인해보고 다양한 상황에 맞추어 구현하는 것이지, 발생하지 않는 상황을 대비하는 것이 아닙니다. 아래 파이썬 코드는 잘못된 예외 처리의 전형입니다.

def some_bullshit(foo):
    if not isinstance(foo, int):
        foo = int(foo)
    
    # ... DO SOMETHING ...
    
def another_bullshit(bar):
    try:    
        # ... 100+ lines of code ...
    
    except CONDITION_THAT_DOES_NOT_MAKE_ANY_SENSE:
        # ... DO SOMETHING ...

한편, 불필요한 예외 처리가 정상적인 경우의 코드 실행에 지장을 주지 않는다면 괜찮을까요? 그렇지 않습니다. 불필요한 예외 처리는 다음에 그 코드를 방문하는 이로 하여금, 그와 같은 예외가 정말로 발생한다고 믿게 합니다. 불필요한 예외 처리는 시간이 지남에 따라 쓸 데 없이 정교해지거나, 서로에 대한 신뢰를 훼손하는 데에 기여할 것입니다. 물론, 우리가 언제나 100% 확신을 가진 채로 구현을 할 수는 없습니다. 시간이 충분하지 않을 수도 있고, 확인해야 하는 것이 너무나 많을 때도 있으니까요. 이런 상황에서 확실하지 않은 예외를 어떻게 해야 할까요? 예상되는 결과가 치명적이라면 더 시간을 투자해 제대로 예외 처리를 해야 합니다. 예상되는 결과가 그리 치명적이지 않다면, 처리하지 않는 것이 낫습니다. 그 상황이 정말로 생긴다면 에러가 발생할 것이고, 에러 정보를 바탕으로 명확하게 해결하면 됩니다.

가짜 확장성: 미래 요건

미래 요건을 고려하여 구현하는 것 역시 확장성이 아닙니다. 현재 요건도 바뀌는 것이 당연한데, 미래 요건까지 예측해서 구현한다는 것은 너무나 현실성이 부족한 이야기입니다. 미래 요건에 대한 구현은 대개 필요 없는 구현에 그치게 될 것입니다. 한편, 미래 요건이 맞아 떨어진다면 시간과 노력을 크게 아낄 수 있는 일이 될까요? 그렇지 않습니다. 구현이 제대로 되어 있을 리 없기 때문입니다. 무언가를 미래의 요건으로 상정하여 구현할 때에는 충분한 계획 없이 순간 순간 떠오르는 것들만을 고려하게 마련입니다. 부족한 일관성과 구체적인 내용에서의 차이로 인하여, 결국 완성도 있는 구현까지 필요한 시간과 노력은 대동소이 할 것입니다.

진짜 확장성: 계획된 확장성

계획된 것에 대한 구현은 확장성 있는 구현입니다. 근시일 내에 production에서 실행되지 않는다는 점에서 앞서 이야기한 미래 요건과 유사하지만, 유의미한 차이점이 있습니다. 계획된 것에는 실체가 있다는 점입니다. 이 실체는 계획의 시작 지점과 맞닿아 있거나, 최종 목적보다 단순화 되어 있는 현재 요건입니다. 실체가 있을 때 계획은 현실성을 갖게 되고, 그 구현은 production 코드에 준하는 구체성을 가질 수 있습니다.

진짜 확장성: 변화에 대한 확장성

변화를 용이하게 하는 것은 확장성입니다. 미래에 무엇을 어떻게 구현하게 될 지에 대해서는 누구도 확신할 수 없습니다. 그럼에도 언제나 확신할 수 있는 것은, 미래에 무엇인가 어떤 방식으로든 변화한다는 것입니다. 그렇다면 가장 효과적인 것은 변화를 받아들일 수 있도록 구현하는 것입니다. 이는 한두 줄의 수정으로 다른 동작을 하는 코드를 말하지 않습니다. 현재 요건의 특수성을 담고 있으면서도, 이를 해치지 않는 선에서 일반성을 담고 있는 구현을 말합니다. 어떤 부분을 참고해야 할 지, 어떤 부분을 어떻게 수정해야 할지 쉽게 판단할 수 있는 구현을 말합니다.

마치며

확장성 있는 구현은 어렵습니다. 이를 덜 고민할수록 개발은 오히려 더 빠르고 편해집니다. 확장성 있는 구현은 잘 드러나지도 않습니다. 확장성 있는 코드는 자연스럽게 이해되고 잘 변화되어, 흔적으로만 남기 때문입니다. 확장성 없는 구현이 오히려 더욱 잘 드러납니다. 쉽게 바꾸지 못하니 오랫 동안 존속하고, 항상 유지보수의 대상이 되기 때문입니다. 마치 중요하고 대단한 구현인 것처럼 보이게 마련입니다. 치열한 고민이 없다면 확장성은 그렇지 못한 것에 파묻힙니다. 개발은 열심히 하지만 문제는 줄어들지 않을 것이고, 확장성을 고민할 수 있는 시간은 더욱 부족해질 것입니다. 악화가 양화를 구축하는 것입니다.

저 역시 이야기한 내용을 언제나 실천하지는 못합니다. 때로는 확인하지 않은 예외 처리를 하고, 함수 전체를 try~except에 넣어 버리기도 합니다. 그러나 확장성을 고민할수록 이를 구분하는 더욱 구체적인 기준을 만들 수 있으며, 이를 실천하는 데에도 익숙해지리라 믿습니다.