회사에서 들어와서 가장 먼저 한 일이 예전 테스트로 돌아가던 어플이 있었는데 그거를 구동하게 하는 것이 첫번째 일이었다.

 

깃헙을 봤을 때 약간 막막한 부분이 바로 보였다.

README.md 에 대한 부분이 아무것도 없었다는 부분과 마지막 commit이 3년전으로 되어있었다는 부분이었다.

 

흔히 말하는 관리가 안되어지는 레거시 코드였다.

해당 어플의 React Native 버전이 69 였고 최신 버전이 79, 버전 10이 차이나는 코드를 건드리기가 약간 두려웠다.

 

그래도 일단은 한번 node module 을 install 을 하고 실행해보았다.

역시나 돌아가지 않는다.

 

그리고 여기에 엎친데 덮친격으로 회사에서 지급해준 노트북은 약 6년전... 맥북(2019)이다 보니 빌드에는 더욱 더 많은 시간이 걸렸다.

한번 돌릴 때마다 10분 정도의 시간을 소비하면서 에러를 보며 문제점을 하나하나 해결해나가고 있었다.

 

그래도 열심히 구글링도 하면서 문제도 해결하고 종속성 문제도 해결하면서 하다보니 한 7일만에 IOS 를 일단 구동시키는데에 성공했다.

 

메인 프로젝트는 구동을 시켰는데 또 문제가 발생한다. 카메라 모듈은 다른 리포지토리에서 가져와서 사용중인데 거기에서 문제가 터진다.

그래서 또 열심히 수정하고 빌드하고 수정하고 빌드하고 반복을 하면서 어찌저찌 해결을 했다.

 

카메라 모듈도 동작하고 AI Module 도 완벽하게 동작을 하게 만들어서 어플이 돌아가는 것을 확인했고

원하는 결과물이 아니었음을 확인하고 이거는 더이상 안쓴다라는 의견으로 되어가서 깔끔하게 commit 을 하고 push를 올렸다.

 

문제는 여기에서 발생한다.

 

라이브러리 종속성에 대한 문제는 해결을 했기에 거기에 대한 이슈는 없었지만 그것보다 어려운게 해당 라이브러리가 문제가 있어서

라이브러리에 들어가서 수정한 코드들에 대해서 기록을 안한 것이었다.

 

그리고 얼마지나지 않고 다시금 그것을 살려서 거기 기반에 한다고 했을 때 빌드가 안되어지는 이슈가 발생했다.

 

그때 아차 싶었다.
README.md 에 내가 헤딩하면서 겪었던 문제들을 적어놓았으면 다음에 했을 때 문제가 없었을텐데

하필이면 그것을 안적고 Commit 을 하고 push 해서 다시 똑같은 문제를 겪게 되었다라고 자책을 하게되었다.

 

그래서 다시금 헤딩을 하면서 README.md 에 적었고 또 다른 사람(혹은 나)들이 이것을 만져야 할 때

나의 헤딩을 반복하지말라는 뜻을 담아 적었다.

 

그렇게 해서 일부를 보여주면 아래와 같다.

android react-native-reanimated (build 이슈로 인하여 3.5.4 -> 3.4.2)
1. boost 관련 
node_modules/react-native/third-party-podspecs/boost.podspec 에서 
```.podspec
spec.source = { :http => 'https://archives.boost.io/release/1.76.0/source/boost_1_76_0.tar.bz2',
                  :sha256 => 'f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41' }
```
같이 변경 필요

node_modules/react-native-reanimated/andorid/build.gradle 에서 
이전 내용
def srcUrl = "https://boostorg.jfrog.io/artifactory/main/release/${transformedVersion}/source/${artifactLocalName}"

변경 내용
def srcUrl = "https://sourceforge.net/projects/boost/files/boost/${transformedVersion}/boost_${BOOST_VERSION}.tar.gz"

node_modules/react-native-vision-camera/andorid/build.gradle 에서 
이전 내용
def srcUrl = "https://boostorg.jfrog.io/artifactory/main/release/${transformedVersion}/source/${artifactLocalName}"

변경 내용
def srcUrl = "https://sourceforge.net/projects/boost/files/boost/${transformedVersion}/boost_${BOOST_VERSION}.tar.gz"


1. RCT-Folly 관련(추후 react update 시에 해결될 가능성 농후)
No template named 'unary_function' in namespace 'std'; did you mean '__unary_function'?
빌드 시 해당 오류 발생 xcode 에서 fix 버튼이 활성화 되어지니 사용하면 해결되어짐


1. React Codegen 관련
```bash
# ios 폴더
rm -rf build
rm -rf Podfile.lock
rm -rf Pods
pod install
```

1. react-native 73 version 으로 올리면서 마주한 문제 및 해결

- react native vision camera 관련 이슈
```bash
A problem occurred evaluating project ':react-native-vision-camera'.
> Cannot invoke method replace() on null object
```

node_modules/react-native-vision-camera/android/build.gradle 에서 337 line 의 내용을 아래와 같이 변경

이전 내용
def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"]

변경 내용
def BOOST_VERSION = "1_85_0" // thirdPartyVersions["BOOST_VERSION"]

- react native vision camera 관련 이슈

빌드시 아래와 같은 이슈 발생
Execution failed for task ':react-native-vision-camera:buildCMakeDebug[arm64-v8a]'.

android/build.gradle 파일에서

이전 내용
ndkVersion = "25.1.8937393"

변경 내용
ndkVersion = "23.2.8568313"

 

그래서 다시금 느꼈다.

 

꼭 README.md에 필요한 부분들을 적자. 만약에 3년전에 했던 사람이 이 부분에 대해서 적었으면 적어도 덜 헤딩을 했을 것 같고,

나 또한 그때 헤딩을 했던 순간에 하나씩 기록했으면 문제가 없었을텐데 기록을 안하니 이러한 사단이 일어나는 것을 보고 실감했다.

 

여담으로,

React Native 에 대한 버전을 바로 한번에 올리지 못하는 이유는 너무 핸들링 할 것이 많기도 했고 시간적 여유가 없었기에 최소한의 시간으로 할 수 있는 부분을 정하다보니 그렇게 되었다.

 

2025년에 2019년 맥북을 가지고 빌드 한번 할때마다 10분씩 소요가 되어지는데... 버전업에 대한 이슈까지 핸들링하기에는 시간자체가 말이 안되게 부족했고 또 장비가 지급해준 맥북 하나(모니터 X, 여타 장비 X)인지라 더더욱 힘들 것이라고 판단을 했기 때문이다.

뭔가 직업관련된 글을 쓰는 것은 매우 어려운 것 같다.

다른 사람들의 블로그를 보거나 혹은 검색해서 들어가게 된 글들의 품질을 봤을 때 매우 뛰어나다.

 

하지만 나는 그 정도의 글을 쓰는 것은 객곽적으로 생각핬을 때 현재의 나에게서는 불가능이다.

 

그렇다고 또, 글을 안 쓰자니 나중에 이러한 일이 있었지라는 부분이 사라지기도 하고

나를 나타내는 부분이 사라지기도 한다.

 

그래서 이제부터라도 짧게 글을 쓰려고 한다.

 

글의 대한 무게감과 다루는 문제의 크기에 상관없이 그냥 글을 쓰려고 하고 또, 매번 아침의 출근을 1시간 정도는 일찍하고 있기에...

(8시 출근인데 아침형 인간이 되어서 7시에 회사를 나오는....)

그때 남는 시간에 글을 쓰는 시간을 가지려고 한다.

 

약간 사견으로 기술관련해서 글을 쓰는 것이 대단하다고 생각하는 것은

- 꾸미는 것(글을 읽을 때 집중력을 가지게 함?)

- 포장 능력(내가 쓰면 3줄이 되는데 그것을 100줄 이상으로 만드는 능력)

- 피로감이 적은 글(말로는 잘하는 데 글로는 못 쓰는 나이기에...)

 

이렇게 3개가 있다.

서론

개인적으로 진행하던 프로젝트 중 축제와 관련이 있는 웹 프로젝트가 있었다.

축제의 디자인 포맷은 대부분 동일하고 변경되어지는 것은 시간표, 장소, 일정, 문구, 이미지 등등... 이 있었는데

한번의 개발을 통해서 여러 축제에 사용 될 수 있는 범용적인 웹 어플리케이션을 구축해야했다.

 

그런데 그때 당시에 읽었던 글 중 요기요에서 어플리케이션 Server Driven UI 를 보고 영감을 받았고 그것을 참고해서 진행했다. https://techblog.yogiyo.co.kr/%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-server-driven-ui-%EA%B0%9C%EB%B0%9C%EA%B8%B0-b1b80f47760b

 

우당탕탕 Server Driven UI 개발기

안녕하세요! 요기요 R&D Center의 BE Developer 정승원입니다. 저는 요기요의 홈 화면을 담당하는 Home Squad에서 백엔드 개발을 하고 있습니다.

techblog.yogiyo.co.kr

 

설계

일단 설계를 하기 이전에 사람이 필수적으로 할 수 밖에 없는 작업을 생각했다.

도메인 연결, 검색 엔진 관련, 프론트 배포 등등이 존재했고 이러한 부분을 제외하고는 시스템 설계 부분에 다 녹여서 진행했다.

프론트

프론트의 설계는 간단했다.

1. 서버로부터 축제정보를 가져와서 렌더링(기본정보, 축제이름, 일시, 장소 등등...)

2. 서버로부터 디자인정보를 가져와서 렌더링(표 정보, 이미지, 설명 글 등등...)

3. 서버로부터 백오피스 정보를 가져와서 렌더링 상호작용(수정, 제거, 등록, 등등... )

4. 모든 API 는 백엔드로 부터 초기에 발급되어진 토큰으로 이루어진다.

예시로

 table: {
        ths: [
            { ...thProps, children: '종목' },
            { ...thProps, children: '순위' },
            { ...thProps, children: '시상' },
            { ...thProps, children: '비고' }
        ],
        trs: [
            [
                { ...tdProps, children: '하프코스 부문', rowSpan: '4' },
                { ...tdProps, children: '1위' },
                { ...tdProps, children: '20만원, 상장, 트로피' },
                { ...tdProps, children: `1-3위 현장 시상\n상금은 계좌이체`, rowSpan: '4' },
            ]
        ]
}

이런 형태의 Json 이 온다면 해당하는 표를 자동적으로 만들어준다.

 

해당 Json 이 javascript (...)같은 경우는 초기 데이터의 경우에는

프론트에서 제공되어지는 것을 기반으로 하고 그 이후에 서버에서 제공되어지는 data를 기반으로 rendering 이 되어지기 때문이다.

 

백엔드

백엔드의 경우에는 해당 축제에 대한 정보 및 디자인 정보, 백오피스 정보 및 사이트 관리를 중점을 두어서 설계를 했다.

1. 프론트에 해당하는 축제 토큰 등록 후 축제 정보 API, 축제 디자인 정보 API 제공

2. 토큰 기반을 통한 축제 별 백오피스 기능 구현(A축제, B축제 축제별 참여인원 관련 API 제공)

 

구현

구현의 경우에는 어렵지 않았고 특별하게 한 것만 적으면

 

위의 Json을 손수 적는 것은 비효율이었기에 Json Generator 라는 함수를 만들어서 간단하게 정보만 입력하면 Json 형식이 나오도록 함수를 제공해줬다.(폰트 관련, 축제 정보 관련)

 

그리고 이메일 관련해서 단일 Thread 로 진행시 Blocking 이 되어 전체 서비스의 영향을 끼쳐 멀티 Thread 를 통해서 진행하도록 변경하여 서비스의 영향을 덜 끼치도록 변경했다.

 

백엔드의 경우에는 서버 하나만 올리면 되어서 끝났으며 프론트의 경우에는 build 이후에 토큰만 발급받아서 S3 를 통해서 올리는 형태로 

백엔드(1) - 프론트(N) 의 형태로 구현이 되었다.

 

후기

Server Driven Design 이라고 말하고 뒤돌아봐서 생각해보니 단순 작업을 줄이는 역할로 치우쳐져서 사용했다는 것을 조금은 느꼈다.

 

현재 설계로 했을 때 가장 큰 장점은 사용자 측면에서 개발과 관련된 수정, 배포 관련을 모르고도 진행을 할 수 있다는 점이다.

축제 관련자 수정 요청 -> 개발자 수정 요청 사항 반영 및 재배포 -> 축제 관련자 확인

이러한 순서를 갖추는데

지금의 장점은 축제 관련자가 문구 수정이나 테이블 수정 간단한 부분은 본인이 할 수 있다는 점이고 재배포랑 관계없이 서버에서 수정만 일어나는 경우 해당하는 부분을 바로 확인이 가능하다는 것이다.

 

그런데 이러한 부분도 있지만 어떻게 보면 중점이 템플릿화에 초점이 맞추어지다보니 프로젝트가 약간의 성향이 벗어난 감이 없지않아있다.

 

 

 

어플을 개발하게 되었던 이유

이전에 인연이 있었던 회사에서 도움을 너무 간곡히 요청을 했었다,(1달에 한번씩 잊을때쯤이면 연락했었다 최소 8번은 했었다.)

 

그러다가 마지막에 내가 그러면 도움을 드리겠다하고, 진짜 터무니없는 적은 금액으로 어플을 개발하기 시작했다.

지금도 그 금액에 어플을 만들었다고하면 대부분의 사람들이 이상하게 쳐다볼것이다.

 

간략한 정보로 보통 받아야 할 최소금액의 1/4 만 받고 진행을 했다.

 

그럼에도 너무 간절하게 요청을 했기에 맘이 약해져서 도움을 주었다.

 

내가 맡은 일

1. 프론트(React Native)

2. 백엔드(Django)

3. 기획 보완(기존 기획에서 말이 안되는 경우를 말이 되게 변경)

4. 시스템 설계

5. 디자인 피드백(기존 디자인에서 유저 불친절한 디자인 변경)

6. 배포

 

진짜로 내가 했던 일이다.

사실 이것만 해도 이미 돈은 버리고 시작했다고 봐도 되는 일이다.

 

그럼에도 진행을 했었던 이유가 어플을 내돈이 아닌 다른 사람의 돈으로 만드는 게 나름의 신선한 경험이 될 거라고 생각을 했고

나중에 또 이게 강점이 될 것이라고 생각했다.

 

 

어떤 어플을 개발

내가 만들었던 어플은 간단하게 말하면

보호자가 피보호자의 위치를 확인하고 맞추어진 스케줄에 그 위치에 존재하는지 파악하고 알람을 보내주는 어플이였다.

위치 기반을 통한 스케줄 알림 어플이였다.

 

React Native 를 통해서 Play Store, App Store 에 등록이 되었고 Django 를 통해서 백엔드를 개발하고 배포는 AWS 에 했다.

 

개발만 하면 편한데...

이게 어플을 말로 간단하게 설명을 했을 때에는 매우 쉬웠는데 진짜 쉽지않은 개발이었다.

 

만약에 기획이 완성도가 있고 또한 디자인도 이상하게 안나왔으면 문제가 없는 개발이 되었을 것이다.

그런데 기획을 처음에 봤을 때부터 말이 안되는 부분이 많았고 또, 미처 발견하지 못하고 개발을 진행하다가 발견한 문제도 많았다.

 

그러다보니 중간에 일을 하다가 다시 기획쪽에 관여를 해서 도움을 주었고 또, 개발을 하다가 디자인 쪽에 요청을 하는 번거로움이 계속 존재했다.

순간순간 그래서 이것을 왜 맡았지라는 생각을 했었던 것 같았다.

 

그래도 한번 맡은 일은 무조건 끝내자라는 신념이 있기에 또 끝내려고 노력은 했다.

 

이때 진짜 중요한게 대화다. 대화를 제대로 못하면 진짜 일이 방향이 이상하게 간다.

특히 여기 회사와 회의를 하면서 매번 느꼈는데 중간에 이상한 말을 꾿꾿이 하는 사람이 있었는데

그 사람 때문에 기를 빨려서 회의가 길어졌었다.

 

개발을 마치고 배포?

일전에도 React Native를 통해서 어플을 개발한 적은 있지만 배포는 하지않았었는데

이번에는 배포또한 내가 직접했었다.

 

그러면서 내가 할 수 없는 문제들은 회사에서 처리를 했어야했는데 그게 세금 쪽이었고 나머지는 내가 다 관리를 해서 배포를 했다.

 

그때 많은 에러사항이 있었다.

안드로이드의 경우에는 문제는 크게 없었지만 로그인 정보를 어떻게 제공해줘야하는 지 명확한 명시가 없어서 약간 헤맸지만 다행히 찾아서 해결을 했다.

 

애플은 솔직히 기억이 안난다. 이유는 너무 많은 리젝트가 있었기 때문이다.

그것 때문에 리젝트가 될 때마다 수정하고 다시보내고 하는 것을 계속했던 것 같다.

 

심지어 가장 큰게 유료결제가 들어있다보니 이게 한층 더 어려움을 더했던 것 같다.

 

내가 만들었던 기능

  1. 약 50개 정도가 되는 페이지 제작(프론트)
  2. 달력 라이브러리 만들기(프론트)
  3. 데이터 피커 라이브러리 만들기(프론트)
  4. 위치센서를 이용해서 데이터보내기(프론트)
  5. 유료 정기 결제(프론트, 백)
  6. 알림 기능 (백)
  7. 위치 정보를 통한 스케줄 확인(백엔드)
  8. 인증 (문자 및 카카오톡 ) (백엔드)
  9. 위치센서 부정확한 위치 필터(백엔드)
  10. 백오피스 개발(백엔드)
  11. 그외 인증관련, 매핑관련(보호자(1)-피보호자(m)), 등등...

이거 외에도 많을 것 같은데 일단은 간추리면서 말하면 저렇게 되어질 것 같다.

 

후기

일단은 어플을 출시하고 그래도 어느정도의 성과는 있었던 것 같다

어플 다운로드수가 출시하고 1,000명을 넘겼었고 리뷰도 4.0 이상대를 기록했었다.(플레이스토어 + 앱스토어)

 

그리고 많은 경험이 있었던 것 같다.

좋은 경험도 있었지만 안좋은 경험이 역시나 더욱 더 기억에 매우 남는다.

 

갑자기 박람회를 가야한다고 일정을 앞당기고 거기에 있었던 휴먼에러를 나한테 원인파악을 부탁하고

결국에는 인증 문제였었는데 문자 혹은 카톡이 안간다는 거 였었는데

그게 문자가 안 가는 경우는 모르는 번호를 안 받는 기능을 켜서 그렇고 카톡이 안되는 경우는 그쪽 계정에 대한 문제로 안 갔었다.

 

그리고 나중에는 말도 안되는 기능을 추가하거나 계약 밖의 일을 가져오는 등등... 을 해서 솔직히 별로 맘 좋게 끝냈던 것 같지는 않다.

 

뭔가 호의로 답했을 때 호의로 돌아왔으면 좋았을텐데 그렇게 하지않음이 너무 아쉬웠던 작업이었다.

 

뭔가 고생은 고생대로 하고, 돈은 돈대로 못받고, 남는 것은 어플밖에 없었던 일이었던 것 같다.

 

그 어플도 아마 거기에서 사후관리를 제대로 못해서 앱스토어는 내려갔었던 것 같다.

 

참고로 어플이름은 스케줄넛이다. (플레이스토어에 나온다.)

 

 

서론

특정 사람의 특정 날짜 이동경로를 취합해서 csv 로 제공해주는 API 가 있었다.

 

그런데 여기에서 문제가 발생한다.

특정 사람의 특정 날짜 이동경로를 csv로 받는데 시간이 너무 걸린다는 것이다.

그때 거짓말 보태서 한 5분에서 ~ 10분 정도가 걸렸던 것 같다.

 

참고로 이 프로젝트의 경우에는 csv 로 받는 것은 관리자가 수동으로 버튼을 클릭을 해야지 동작을 한다.

그렇게 많은 사람들이 호출하는 API는 아니었다.

 

그래도 이거는 큰 문제였다.

 

그런데 해당 소스코드를 봤을 때에는 문제를 바로 확인을 할 수가 없었다.

 

이유는 초기에는 이렇게 오래걸리는거면 N+1문제라고 생각을 했고 그쪽으로 확인을 하려고 할 때

기존 개발자가 그거는 문제없이 처리했다고 했고 그래서 그부분을 배제하고 코드를 봤었다.

그리고 이거외에도 나는 다른 일을 하고있어서 그렇게 시간을 할애할 수가 없었고 그러다보니

언뜻보고 넘어가면서 왜지? 라는 의문을 남긴체 넘어갔다.

 

그때 로직이 어떻게 되었냐면

1. 해당 사람, 해당 날짜의 이동경로 내역을 가져온다.

2. 이동 경로에는 이동수단, 시간, 평균 속도, 평균 가속도 등등이 표현되었다.

3. 이동 경로에 연결된 위치 정보를 가져온다.

4. csv 에 입력 후 저장

 

그런데 이게 하나의 함수 안에 있어서 파악이 어려웠다.

 

그러다가 나에게 할당된 일을 대부분 마치고 컨펌을 기다리고있는 남는 시간에

아직도 해당 문제로 골머리를 앓고있는 개발자를 보고 다시 코드를 살피면서 문제를 파악했다

문제해결

일단 접근 방법은 기능을 하나씩 빼면서 진행하는 형태로 했다.

기능순서의 역순으로 하나씩 배제하면서 시간을 찍었다.

csv-> ... -> 이동경로 내역 순으로 진행을 했다.

 

그렇게 하다가 문제를 발견했다.

기존에 N+1 문제를 해결했다고 했던 개발자의 말을 믿고 그 부분을 대충보고 넘어가기도 했고 변수명이 진짜 절묘하게

되어있어서 문제를 파악하는데에 오래걸렸다.

진짜로 나중에는 1줄씩 주석처리를 하면서 문제를 파악했다.

 

요지는 이랬는데

1번 시점에서 이동경로 내역을 가져오면서 eager loading 을 통해서 연결된 위치정보를 가져왔다.

 

3번 시점에서 이동경로에 연결된 위치 정보를 eager loading 을 통해서 가져온 걸 안쓰고 다시 DB에 query를 날려서 가져왔다

(lazy loading).

 

그렇게 되어지다보니 그때 이동경로에 포함된 위치정보가 약 5만개 정도였는데 그게 N+1 문제가 터져서 속도 저하의 주범이 된 것이였다.

 

근데 진짜 정말로 언뜻보면 눈에 진짜 안띄게 작성이 되어있었다.

 

그러다보니 찾는데에 진짜 애를 많이 먹었었다.

 

그런데 나중에는 진짜 엄청 작은 단위를 주석처리하다가 얘는 왜 이렇게 작성되었지 하면서 지우고 나니까 속도가 정상으로 나왔었다.

 

그때 문제 파악하고 수정을 해서 진행을 하니 동작이 잘 되었고 또한 겸사겸사,

코드 리팩토링을 통해서 조금 더 보기쉽게 작업하고, 여러번 db에서 가져와 연산을 하는 작업을 한번 db에서 가져오는 형태로 변경했었다.

 

 

결론

일단 속도는 정상적으로 나왔다.

그렇지만 이 문제를 해결하기 위해 할애했던 시간이 그렇게 크지는 않지만 짬짬히 한 시간을 합치면 그래도 1~2일 정도는 되었을 거다.

 

그러면서도 이 문제가 그렇게 오랜 시간을 걸려야했던 문제였을까?

라고 생각하면 내 생각은 그렇지가 않다.

 

왜냐하면?

이 문제같은 경우는 N+1 문제라기보다는 너무 복잡하게 짜여진 코드가 문제였다.

 

이렇게 말하는 이유는 간단한 예시로 요리를 들자면

 

재료를 마트에서 사오고, 요리 시작(준비, 다듬기, 본요리), 접시에 담아서 내놓기 이렇게가 순서라면

그때의 소스코드 작성은 재료를 마트에서 사왔다가 요리를 하다가 다시 재료를 마트에서 사오는 것의 반복이었기 때문이다.

 

그러다보니 소스코드를 읽는 입장에서도 매우 힘들고 문제 파악은 더욱 더 힘들다.

 

DB 에 원하는 정보를 가져오기 => 받은 정보를 통해서 계산 => CSV 에 담아 저장하기

이렇게 진행을 했어야 했다면

 

소스코드가 DB에서 원하는 정보가져오기 => 받은 정보를 통해서 계산 => 부족한 게 있네 다시 DB에서 가져오기 => 받은 정보를 통해서 계산 (반복)

 

이렇게 하다보니 문제가 발생할 수 밖에 없는 형태였다.

 

아마도 개인이 맡아서 하는 프로젝트라 코드를 의식에 흐름대로 짜다가 나중에 개발이 완료되어지고나서 리팩토링이 안되어지다보니

이러한 문제가 발생했던 것 같다.

 

그러다가 다른 개발자(본인)이 봤을 때 보기가 어려워서 더욱 더 그랬던 것 같다.

서론

기존 주식관련 정보제공 플랫폼에 관련하여 일을 진행한 적이 있다.

 

그때 배포환경이 EB로 구축이 되어있었고 비용이 월 약 100만원에서 200만원 사이로 나왔던 것으로 확인이 되었다.

그리고 사용자는 유료 사용자가 100명 정도가 있고 무료 사용자의 경우 약 1,000명 정도가 있었다. 

 

그래도 나름 초기 플랫폼치고 서비스의 이용자가 있는 플랫폼이였다.

 

그런데 나는 사실 여기서 보면 의아한 부분이 있었다. 대략 사용자가 1,000 명 정도인데 왜 서버 비용이 저렇게 많이 나오는가?

 

내가 생각 했을때에는 월 30만원 정도로 절감할 수 있다 생각했다.

그리고 또 하나의 문제가 무엇이냐면 특정 기능을 수행하면 서버가 순간 다운되었다가 다시 스케일링 되는 형태였다.

이때 지연시간이 존재했고 그로인해서 다른 사용자도 영향을 받게되는 것이였다.

물론 스케일링이 되고 시간이 지나면 괜찮아졌지만, 뭔가 매끄러움은 없었던 것 같았다.

 

그럼에도 여기에 대해 바로 개선하기에는 조금은 어려운 상황이기는 했다.

 

이유

기본적으로 리스크가 있는 작업이었고(기존 사용자가 있는 상태에서 어떠하든지 일정시간의 장애를 경험할 수 밖에 없는 상황),

그리고 우리나라 주식 뿐만 아니라 해외주식까지 다루고 있다보니 변경할 수 있는 시간을 잡기가 어려웠다.

 

사용자가 없으면 사실 그냥 배포환경을 바꾸는 것에 그렇게 크게 어려움은 없었을 것 같다.

 

현재 상황 요약

문제점

1. 비용이 너무 많이 나온다

2. 하나의 기능 때문에 오토 스케일링이 일어나고 그 동안에 지연시간이 발생 -> 비용 증대 요인 중 하나

 

해결방법

1. 기존 서버의 사양을 적절한 테스트를 통해서 설정한다

2. 특정 기능만 따로 분리하는 형태로 변경 후, 기존 서버의 사양을 대폭 낮춤 

3. 서버리스 형태로 변경하기

 

선택에 도움을 줬던 부분

1. 서버에 그렇게 많은 노력을 기울일 수가 없다(인력부족).

2. 비용의 문제는 현재 심각하다.(기존에는 크레딧으로 결제 중, 이게 소진되면 바로 지출로 잡힌다.)

3. 전체적으로 AWS 에 그렇게 많이 친숙하지 않은 사람들

(해당 서비스를 외주를 통해서 납품을 받고 사용중, 외주측에서도 관리 측면으로는 도움을 줄 수 있는 환경이 아니었다)

 

해결방안

사실 스트레스 테스트를 통해서 적절한 사양을 찾는 것도 좋은 방법이 될 수도 있었을 것 같다.

서버에 관심이 있는 사람이 회사내에 있었다면 말이다.

 

하지만 현실은 그렇지 못했고 서버에 노력을 많이 할애할 수 있는 환경이 아니었다.

그래서 lambda 를 통해서 배포환경을 변경하게 되었다.

 

이유는 기존에도 Lambda를 통해서 많은 프로젝트들을 진행했었고

EB 같은 경우에는 설정하는 부분에서 많은 시간을 할애해야하다보니

지금같이 관리의 측면에서는 매우 벗어나는 영역이었다.

그러다보니 Lambda를 선택하게 되었고 또한, 분리라는 선택지를 고를 수도 있었지만

미래에도 이러한 경우가 생길 경우에 또 다시 이러한 대응을 해야할 환경이 생겨지다보니 일단은 잠깐의 시간유예를 두어서

조금 더 깊게 생각해보는 방향으로 진행하게되었다.

 

시간을 늘리기위해서는 크레딧 소진되는 시기를 뒤로 미룰 필요가 있었기 때문이다. 

 

실질적으로 Lambda를 통해서 배포하는 중에는 그렇게 크게 문제는 없었다.

워낙 많이 했던 작업이기도 했고 다행히 version 또한 맞았기에 따로 어려움이 없었던 작업이었다.

Zappa 를 통해서 배포를 진행했고 해당 주식장이 아예 멈추는 주말에 작업을 진행했다.

 

그리고 어떻게 배포를 했는지에 세세하게 적지않은 이유는 나보다 더 잘 쓴 사람들이 요즘은 너무 많다.

그냥 키워드만 던지고 검색만 하면 요즘은 손쉽게 찾을 수 있는 것 같다.

 

결론

결과는 우리가 원하는 예상치대로 나왔다.

시간을 벌게되었고(크레딧 소진시기를 미루게 되어짐), 신기능 개발에 몰두할 수 있었다.

 

이 문제가 발생한 가장 큰 요인은 외주를 통해서 받았을 때 적절한 사양으로 받지 못한 것이 가장 큰 요인이었다.

처음부터 적절한 사양으로 받았으면 아마 시간적 여유가 생겨서 더 나은 방안을 모색했을 것 같았지만

현실은 시간에 문제에 부딪혀 가장 익숙한 방법으로 해결하게 된 방법이기 때문이다.

 

그래서 그 부분이 아쉽기는 하다.

왜냐하면 그쪽에서는 템플릿 형태로 찍어내는 배포환경을 갖추다보니 문제가 생기면 템플릿을 바꾸어서 하다보니

적절한 세팅을 못한다는 것이 있고

그쪽에서는 따로 AWS 에 대해 잘 다루지도 못하고 다른 사람들에게 받은 설정파일을 통해서 작업을 하다보니

대응을 못하는게 매우 아쉬웠던 것 같다.

서론

기존 ERP를 기반으로 해서 새롭게 ERP 를 만들게 된 경험이 있다.

 

그때 기술 스택을 React + Django 로 개발 진행을 하게되었고

그때 내 밑으로 있었던 신입 개발자와 나는 사수로서 함께 작업을 하게 되었다.

 

그때 신입 개발자가 따로 어디쪽으로 개발을 진행한 경험이 없고 전공자이기만 해서

내가 프론트로 React를 해보는 게 어떻겠냐라고 제안을 했다.

왜냐하면 그전에 작업물이 그래도 node.js 기반으로 진행을 했기 때문에 그쪽이 더 적합할 것으로 판단했다.

제안하고 동생도 승낙을 해서 React(개발자 신입) + Django(본인) 이렇게 개발을 진행했다.

 

기한은 넉넉하게 잡아 2달이었고, 물론 기한내에 하기위해 내가 백엔드 작업을 빨리 끝내고 프론트를 도와주는 형태로 작업했다.

 

문제 발생

그러면서 막바지에 문제가 하나 생겼다.

 

처음에는 기존 테스트 데이터들만을 가지고 진행을 하다보니 데이터의 수가 그렇게 많지 않아 전체 주문에 대한 금액 계산이 오래 걸리지가 않았다.

 

그런데 기존에 있던 데이터베이스의 양이 많았고 옮기고 테스트를 돌려보니 시간이 너무 오래 걸리는 것이였다.

 

그때의 DB가 간추려서

주문내역(날짜, 구매자, 수금금액, 할인율, 부가세) - 아이템(수량, 금액, 이름, 입고일, 출고일, 입고금액) 이렇게 있었다.

 

계산방식

주문내역의 총액은 해당 주문내역을 부모키로 가진 아이템을 가져와 sum(아이템 별(수량 * 금액)) 이런 형태로 가져왔고(subquery)

 

다시 여기에 할인율, 부가세를 가져와 계산을 진행 후 수금이 되어진 금액을 통해서 만약에 수금이 안되었으면 -, 수금이 되었으면 + 로

나타내었다.(앱 내에서 처리)

 

그리고 주문금액의 총합이 전체 매출액으로 계산이 되어졌다.

 

그런데 당초 진행을 했을 때의 데이터는 100개 정도로만 하다가 갑자기

주문내역이 200,000건 정도로 늘어나니 총액 계산에 약 1분 30초정도가 소요되었다.

 

임시방편

그래서 내가 여기에서 먼저 진행한 것은 API 분리를 먼저 했다.

 

총매출액 계산이 포함된 API 호출이 되어지는 페이지가 로그인이 되면 제일 처음에 마주하는 대시보드 페이지였다.

그 페이지에서 GET API 호출을 했는데 다른 항목들은 준비가 되었지만

총매출액 계산에서 저렇게 시간이 오래걸리니 다른 곳에 영향을 끼쳐 렌더링이 되어지지않은 문제가 발생하니

API 를 따로 먼저 분리를 진행하고 개선을 시도했다.

 

일단은 그때 당시에 저 부분의 경우에는 먼저 subquery를 통해서 주문내역에 들어있는 총합은 계산이 되어져서 나오고

그다음에 계산들은 앱내에서 계산을 진행했다.

 

그렇게 하게 된 이유는 Subquery로 전체적으로 계산하기에는 일단 query가 너무 길어져 보기가 힘들기도 했고 속도개선이 그렇게 되어지지는 않았다.

 

그렇다보니 그냥 절대적인 양에 따른 시간이 문제임을 깨달았고 다른 방식으로 이 문제를 해결을 진행했다.

 

문제 해결

일단 절대적인 양 때문에 시간을 큰 폭으로 개선을 할 수 없음을 깨닫고 진행한 방식은

Redis 를 이용해 처음에 계산한 전체 매출액을 저장했다가
전체 매출액에 영향을 끼칠만한 행동이 있을 때 업데이트를 진행하는 방식으로 개선했다.

 

그렇게 될 경우에 처음에 1분30초 정도로 걸리고 다음에 호출을 할 때에는

조회에서는 당연히 Redis 에 접근하는 시간만큼 들 것으로 예상했고, 다른 작업에서는 조회 + 계산 + 저장 만큼의 시간으로 들것으로 판단했다.

 

예시로

만약에 주문 내역이 발생하면 주문내역의 금액 계산 진행 -> Redis 에 있는 전체 매출액을 가져오고 그 값을 계산 후 Redis 에 저장

기존에 (+)10,000 매출액이 있고 주문내역으로 (+)2,000이 발생한 경우 (+)10,000 + (+)2,000 = (+)12,000을 Redis 에 저장

 

이렇게 Update, Create, Delete 부분에 해당 로직을 적용시켰다.

 

결과

일단 결과는 매우 만족스러웠다.

 

예상했던대로

기존 1분 30초가 걸리는 API가 사실상 100ms 이내로 동작을 하게되었다.

(해당 부분 Redis에 정보만 가져오는 것만 했으면 더 짧았을 것이다.)

 

GET API 가 하는 것은 진짜 별것이 없었다. 그냥 Redis 에서 데이터만 가져오는 것이면 되었기 때문이다.

 

그리고 주기적으로 사람이 이용을 안하는 때(새벽)에 Cron을 이용해서 Redis 에 있는 값이 맞는지 검증하는 로직도 돌렸었다.

만약에 맞지않을 경우에는 업데이트 후 알림을 보내도록 했었는데 딱히 알림이 오는 일은 없었다.

 

이게 절대적인 데이터의 양이 많아지면 어쩔 수 없이 시간이 걸리는 문제가 발생한다.

뭔가 offset pagination 같은 느낌이다.

그냥 읽기만 해도 시간이 걸리는 느낌?

그런데 거기에 계산을 해? -> 더 걸린다. 이 느낌이다.

 

그래도 이때 프로젝트가 의미있었던 시간이었던게 해당문제를 처리하는 방법이 많이 있었겠지만

빠르게 문제 요인을 파악하고 그때 당시에 가장 적절한 방법을 취했던 것 같았다.

그리고 그것을 검증하는 로직을 통해서 이상이 있을 경우에는 해당 부분을 다시 검토 후 저장하다보니

문제가 발생할 수 있는 순간도 줄였던 것 같다.

 

그리고 처음으로 누군가를 가르치면서 했던 경험이라서 내가 새롭게 배운 느낌도 있었다.

뭔가 내가 처음에 React를 했을 때에는 경험했던 부분들에 대해 의문이 안 들었던 부분이 있었는데

신입 개발자가 의문을 가지고 질문을 했을 때 일단 내가 생각했던 느낌을 말하고 그래도 확실하지는 않기에

문서를 찾아서 다시 재확증하는? 경험을 많이 했는데 그러면서 다시 재각인하는 시간이 되었던 것 같다.

 

서론

반도체 MES(React+Django) 관련 프로젝트를 도와줬던 때의 일이다.

 

해당 프로젝트의 경우에는 기한이 약 2달 정도 남아있었고

그때 설명으로 들었을 때에는 거의 개발이 완료되었고 버그 부분만 수정을 해주면 된다고 이야기를 들었었다.

그래서 나의 입장에서는 음... 쉽겠다라고 생각을 하고 일을 진행했었다.

 

그런데 그때 당시에 그 일을 맡고 경악을 금치 못했던게 세번이 있었는데

첫번째는 서비스의 속도가 너무 느리다는 것

두번째는 코드의 퀄리티가 너무 떨어졌다는 것

세번째는 거의 개발이 완료되었다는 것이 거짓말이었다는 것

 

서론 해결

일단 서비스의 속도의 원인은 70%(React) + 30%(Django) 였다.

일단 React 의 경우 쓸데없이 전체부분이 재렌더링이 되어지다보니

속도저하 이슈(5초)가 생기고 있었고 그것을 다시 컴포넌트화해서 해당 부분을 필요한 부분만 재렌더링되게 개선했다.

이렇게 했을 때 다행히 속도 저하가 많이 줄었고(1초 이내로) 일상영역에서 쓸수있는 범주내로 만들어졌다.

그리고 Django의 경우 DB 관련해서 bulk로 처리해야 할 것들이 각각 하나로 처리되어지다보니 그 속도가 매우 느렸다.

참고로 bulk로 해도 느리기는 했을 것이다. N+1 문제도 발생하고 있어서 해당 부분을 prefetch (상황이 역참조) 를 사용해서 해결을 했다.

 

두번째의 경우에는 솔직히 전체를 건드리지는 못했고 필요한 경우에만 내가 개선을 진행했었다.

왜냐하면 누군가는 유지보수를 해야할텐데 코어에 있는 코드들이 너무 지저분하면 사실 그 누구도 일을 하기 싫을 것 같아서

어려운 부분은 주석을 달면서 진행을 했고 코드만으로도 나름 의미가 해석되게 작성했었다.

 

세번째의 경우에는 총 공정이 31개의 공정을 관리를 해야한다면

이 프로젝트를 착수했을 때 공정이 약 15개 반?만 관리를 하는 코드가 작성이 되어있었다.

사실 이때 뭔가 문제가 있는 것이기에 애매했지만 그래도 진행을 했다.

하나의 웨이퍼가 있으면 그것을 통해서 만들어지는 완성품을 이력관리를 해야한다.(품질, 수율, 폐기율 등등...)

그런데 특별한 공정때마다 그 부품이 쪼개지는데 만약에 1개의 웨이퍼가 있으면 어떤과정을 지나면 16개의 다이?로 쪼개졌었다.

그리고 또 어떤 공정을 지나면 쪼개지고 그래서 최종적으로 1개의 웨이퍼에서 256개의 칩? 으로 가는 과정을 이력을 남기고 했어야했는데 그 특별공정이 16번 부터 일어나는 데 그것이 안되어있어서 문제가 있었고 그것을 구현했었다.

 

본론

앞에서 말했듯이 공정이 15개 반으로 되어있었다라고 말했었다.

이유는 여기에서도 진행을 하다가 멈추었기 때문이다. 이유는 다른 일들이 겹쳐서 바빠졌기 때문이다.

뭐 그래도 되어있는 코드를 보고 나도 어느정도 감을 잡으면서 하고는 있었기에 도움이 되었다.

 

그런데 문제가 생겼었다.

원래 기존에 있던 코드를 통해서 구현을 하고 다음 공정에 부품이 쪼개지는 것을 확인해야하는데

문제는 이전 부품은 완료처리가 되었는데 중간에 부품이 쪼개져서 생성이 되지않는 것이었다.

 

그래서 해당하는 부분을 찾으려고 처음부터 진행을 했었는데 동일하게 오류가 발생을 했으면 좋겠지만 또 그러지는 않았다.

이럴때가 난감하다. 차라리 무조건 발생하는 오류라면 빠르게 원인파악을 하고 문제를 해결하는데 그러지 못했던 것이다.

 

문제는 이러했다.

종종 16번 공정에서 이전 부품에서 완료처리는 되었지만 파생 부품이 생성이 되지 못하는 오류가 발생

 

본론해결

일단 문제 파악을 위해서 나는 전체 순서를 테스트하는 코드(1번 부터 16번까지의 공정 테스트 코드)를 작성했다.

이유는 이전 사람들이 테스트코드없이 하나하나 작업을 했고 이게 참 노가다랑 다를 게 없었다.

만약에 16번에서 케이스가 발생을 안하면 다시 1번부터 시작을 하는 기이한 일이 발생하기 때문이다.

 

이렇게 테스트 코드를 작성해서 돌려보니 문제를 빠르게 파악을 할 수 있었다.

 

문제는 웨이퍼에 초기에 시리얼 넘버같은게 붙는데 이 시리얼 넘버를 통해서 앞으로 붙을 부품에도 웨이퍼 시리얼넘버 + 부품 넘버 이러한 형태를 띄게되어진다.

 

그런데 이러한 과정에서 만약에 적법한 시리얼넘버가 들어오지 않는 경우에는 에러를 발생하게 되어지는데 이러한 부분들이 기존에 정확하게 작성이 되어있지 않아서 문제가 발생했던 것이다.

 

그러다보니 이전 작업에서는 완료가 되었지만 다음 작업에 생성될 부품들이 생성이 되어지고 그 다음에 외래키가 설정되어져야하는데 해당 부분이 오류를 통해서 중간에 문제가 생기다보니 누락이 되어지는 부분이 생긴 것이다.

 

이것 또한 단일로 처리되어지다보니 만약에 16개가 생성이 되어져야한다면 11개쯤에서 생성하다가 오류가 발생해 터졌던 것이다.

그러니 누락이 되어진것이였다.

 

그래서 내가 했던 경우는 다음과 같았다.

1. 시리얼 넘버 작성 기준과 시리얼 넘버가 이상한 경우 대응 방안

2. 원자성 부여로 중간에 문제 발생시 롤백을 시키는 방안

3. 1~31 번 까지의 공정 관리 코드 작성 및 테스트 코드 작성

 

결말

다행히 일은 별 문제없이 마무리 되었고 성능도 많이 개선이 되어서 나름 만족을 하면서 작업을 했었다.

사실 성능은 10초에서 1초 안으로 줄이는 것은 의외로 쉬운 경우에 속하는 것 같다.

진짜로 계산 양이 방대하지 않은 경우에는 보통은 소스코드에 문제가 있기 때문이다.

물론 이번 프로젝트의 경우에는 계산이 많기는 했다.

아마 저기가 한달에 쌓이는 데이터를 봤을 때 1억개 정도는 쌓였던 것 같았다.

그래서 초기에 어느정도 DB 작업을 하면서 고려하면서 진행을 했지만 현재는 잘 되어지고 있는지는 모르겠다.

그리고 이 프로젝트를 통해서 치질을 얻어서 3일간 고생했다. ㅠㅠ

+ Recent posts