CHAT

문제를 해결하는 방법 - 간단명료한 문제 정의

nooblette 2024. 4. 8. 22:11
반응형

목차

    배경

    지난달에 회사 업무 중에 배치 프로그램 하나를 수정하며 리팩토링까지 진행한 적이 있다. 로직이 엄청 복잡하거나 논리적으로 어려운 배치는 아니었지만, 주요 로직 중 하나로 다른 개발 팀이 데이터를 넣어주는 테이블이 있었고 또 다른 개발팀에게 POST 메서드로 API를 요청해서 데이터를 처리하는 부분이 있었다. 또한 이 배치 수행 결과는 다음 배치 수행에까지 영향을 주고 있었다.
     
    프로그램의 주요한 로직이 이처럼 다른 개발팀과 팀 내 다른 배치와 긴밀하게 얽혀있다보니 자연스럽게 성공하는 케이스 이외에도 실패할 때는 어떻게 동작할지 고민하게 되는데, 기존 코드상으로는 실패할 때 어떻게 동작할지 / 지금 방식대로 동작하는 게 적절할지 / 개발자가 어디까지 인지해야 할지 / 배치 오류 발생 시 개입을 최소화하고 자동화하는 방안은 없을지 / 장애 상황 발생 시 어떻게 처리해야 할지 등 한 번에 많은 사항들이 고민이 되며 혼란에 빠지기 시작했다.
     

    내용

    리팩토링의 필요성을 느끼고 코드를 분석하고 성공과 여러 실패 케이스에 대해 고민하기 시작하면서 머릿속은 금방 복잡해졌다. 현재 코드 상으로는 처리 도중 로직이 실패하거나 API가 실패하는 등 예상과 다른 경우가 발생하면 어떻게 동작하는지 파악하기 위해 디버깅을 해보며 지금과 같은 방식으로 동작하는게 적절할지 더 나은 방안은 무엇이 있을지 고민이 되었다. 이 외에도 개발자가 인지할 필요성이 있는 부분인지, 만약 장애가 발생하면 어떻게 대응해야 할지, 개발자가 개입을 최소화하고 자동으로 복원할 수는 없을지, 이렇게 대응하는 게 적절할지 등 많은 내용들이 고민이 되었는데 한 번에 모든 상황을 고민하다 보니 생각도 많아지고 불안감 내지 공포스러운 마음까지 들었다.
     
    여러 케이스를 한 번에 계속 고민하고 해결하려고 하다보니 머릿속은 더 복잡해지고 시간이 지체되어 마음도 조급하고 불안해졌다. 지금까지 잘 동작했는데 꼭 리팩토링을 해야 할까, 내가 과하게 모든 엣지 케이스까지 다루려고 하는 것은 아닌가, 원복하고 기존대로 두는 게 오히려 더 좋지 않을까 등 여러 생각이 들기 시작할 때쯤 문득 내가 해결하고자하는 문제를 먼저 분명히 정의하지 않았다는 생각이 들었고, 이로 인해 혼란스러운 상황이 계속되는 것임을 깨달았다. 이러한 생각이 들고나서는 분석하던 내용을 기록하던 것을 멈추고, 다시 가장 처음으로 돌아가 지금 리팩토링 중인 이 배치의 어떤 부분이 불안해서 고민을 하고 있는지, 내가 지금 해결하려고 하는 문제는 무엇인지부터 다시 명확히 정의하려고 했다.
     
    문제를 정의하기 위해 가장 작은 단위부터 고민을 해보았는데, 사실 고민이 되던 부분은 두가지였다. 첫 번째는 다른 팀이 데이터를 넣어주는 테이블이었고, 두 번째는 내가 또 다른 팀에 POST API를 요청하는 부분이였다.
     
    먼저 다른 팀에서 데이터를 넣어주는 테이블에 대해서 생각해 보았다. 다른 팀에서 협의한 시간에 정해진 데이터를 성공적으로 넣어만 준다면 더할 나위 없이 좋겠지만 분명 그렇지 못한 순간들이 발생할 것이다. 내가 고민이 되던 것은 기존에 협의된 시간과 일자에 정해진 데이터가 들어오지 않는 케이스였다. (데이터를 넣어주는 팀에서 발생한 장애로 데이터를 넣어주지 못하거나, 전사 장애로 DB에 오류가 발생한다거나, 배치 프로그램 수행 환경에서 문제가 생긴다거나 등 예측할 수 없는 어떠한 원인으로 데이터가 들어오지 못할 수 있을 것이다.) 이 문제에 대해서는 데이터를 넣어주는 팀에게 미래 데이터까지 한 번에 미리 받아두기로 협의하고 만약 배치가 수행 중인 날짜로 데이터가 없거나 기획자와 협의한 특정 비율보다 더 적게 있다면 데이터가 제대로 들어오지 않는 것으로 판단하기로 문제를 정의해서 고민을 해결할 수 있었다.
     
    두 번째로는 또 다른 팀에 API를 요청하는 부분이었다. API 요청은 단순 1회만 일어나는 것이 아니라 특정 List에 loop를 돌리면서 각 원소들에 대해 GET API를 요청해서 어떠한 값을 받아오고 그 값을 기반으로 POST API까지 일어나는 것이 하나의 프로세스였다. 사실 이 부분 때문에 가장 많은 고민이 되었는데, 만약 수행 도중 일부는 처리가 완료되었는데 다른 일부를 처리하던 중에 상대 API 서버가 뻗어버리면 어떻게 대응해야 할지, 배치를 중단하는 게 적절할지 아니면 loop를 돌리고 있기 때문에 다음 원소에 대해 우선 시도해야 할지, POST 요청인데 재처리가 필요하다면 어떻게 해야 할지(중복 처리가 염려되었다) 등 여러 케이스를 한 번에 고민하다 보니 더욱 불안해지고 있었다.
     
    이 부분도 첫 번째 상황과 유사하게 가장 작은 단위부터 문제를 정의하기 위해서 다시 가장 기본적인 문제가 무엇인지 고민해 보았다. 결국 내가 고민하던 것은 API 요청을 성공했을 때와 실패했을 때 어떻게 처리할지, 그리고 어떤 경우에 실패로 보고 어떤 경우에 성공으로 판단할지였다.
     
    우선 API 요청의 경우, 요청이 성공하는 경우(http status가 200으로 내려오는 경우)와 클라이언트 에러(4xx 번대 에러)가 발생하는 경우 그리고 서버 에러(5xx 번대 에러)가 발생하는 경우로 나누어서 문제를 정의했다. 문제를 정의하고 나서는 각 3가지 케이스에 대해 어떻게 처리할지 고민하였다. 예를 들어 성공하는 경우엔 다음 로직을 실행하면 될 것이고, 클라이언트 에러가 발생하는 경우 강제로 특정 값을 세팅하고 다음 로직을 수행한다, 서버 에러가 발생하는 경우 배치를 중단하고 즉시 개발자가 인지할 수 있도록 한다. 와 같이 각 케이스 별로 처리 방안을 마련했다.
     
    그다음으로는 성공과 실패를 정의했다. List의 모든 원소에 대해 처리를 성공하면 배치가 성공으로 동작하는 것이고, 실패하면 실패로 동작하는 것은 당연하지만, loop를 돌리면서 각 원소별로 API 요청을 전송하는데 그중 일부만 성공하는 경우는 배치 수행 결과를 성공으로 볼지 실패로 볼지가 명확히 정해져 있지 않았다. 이 배치 수행 결과는 다른 배치의 수행에도 영향을 주고 있었기 때문에 어디까지를 성공으로 판단할지 먼저 정의했어야 했다. (결국 이처럼 원소의 일부만 처리를 성공하는 경우엔 성공으로 볼지 실패로 볼지와 같은 문제가 명확히 정의되어 있지 않았기 때문에 자연스레 해결책 또한 쉽게 산출하지 못하고 더욱 혼란스러워졌다.)
     
    이 부분에 대해서는 API 처리 결과와 연관 지어서 성공과 실패를 정의할 수 있었다. 예를 들어 만약 배치 수행 로직 중 API 요청과 무관한 부분에서 동작하지 않는다면 이는 배치가 실패한 것이다. 하지만 만약 loop의 일부 원소는 API 요청을 성공했지만 이후 원소들에 대해 클라이언트 에러(4xx 번대 에러)가 발생하면 이는 성공으로 판단하고 다음 프로세스를 진행한다. 상대 서버가 5xx 에러를 내리는 경우에는 수행 도중이어도 즉시 실패로 판단하고 중단했다. 그러고 나서 배치를 재수행한다. 이렇게 문제를 작은 단위부터 정의하고 해결 방안을 고민하다 보니 배치를 재수행할때는 POST 요청이 중복으로 발생하지 않도록 대응하는 방안 또한 마련할 수 있었다.
     

    결론

    매번 정해진 시간에 다른 개발팀에서 테이블에 데이터를 넣어주고 API 요청도 항상 성공하고 상대 서버도 항상 장애가 발생하지 않는 상황이라면 너무 좋겠지만 분명 어떤 예상치 못한 이유로 의도와 다르게 동작하는 경우는 발생할 것이다. 어떤 원인으로 발생할지 정확히 예측하고 대응하는 것은 불가능하지만 어떠한 원인이 나오더라도 그에 따른 적절한 대응책과 해결 방안을 고민해 두고 코드로써 구현해 두는 것은 필요하다고 생각한다.
     
    처음에는 한 번에 모든 케이스를 고민하고 대응하려고 하다 보니 더욱 혼란스러웠고 잘하고 있는 게 맞을지 걱정스러운 마음과 불안한 마음이 앞섰지만, 문제를 작은 단위부터 정의하고 해결방안을 마련하보니 좋은 경험을 할 수 있었던 것 같다. 지금 생각해 보면 내가 처음에 고민하며 혼란스러웠던 내용들(어떻게 해결해야 하는지, 지금 방안은 적절한지, 더 나은 방안은 없는지, 다른 케이스에서는 어떻게 동작하는지, 만약 장애가 발생하면 어떻게 대응해야 할지 등)은 가장 작은 단위부터 지금 해결하고자 하는 문제가 무엇인지 먼저 정의하고 나서 고민해야 하는 문제들이었다.
     
    이번 케이스에서 항상 내가 마주한 상황에 대해 문제를 작은 단위부터 고민할 필요성을 느꼈다. 가장 중요한 것은 지금 마주한 문제가 무엇인지 분명히 정의하고 나서 가장 기본적인 기능부터 동작을 정의하는 것이라는 것을 느꼈고, 그 후에 내가 해결하고자 하는 문제의 구체적인 해결 방법을 고민해 봐야 더욱 좋은 문제 해결이 가능한 것을 느꼈다. 실제로 이 이후에 해결 방법을 먼저 고민하다가 혼란에 빠질 뻔했던 적이 있는데, 이때의 경험을 교훈 삼아 내가 해결하고자 하는 문제가 무엇인지부터 다시 정의했다. 그리고 그 후에 가장 기본적인 기능부터 동작을 생각해 보았고 이 동작을 구현하는데 어떤 상황을 꺼려하고 어떤 결과물을 얻고 싶어 하는지 고민해 보며 구체적이고 만족스러운 해결방안을 빠르게 도출해 낼 수 있었다.

    반응형