언제나 디자인을 구현하는 것은 매우 귀찮은 일이다.

그래서 주로 백엔드 작업을 선호하고 직무도 거의 그렇게 해왔다.

 

그러나 매번 하고싶은 일만 할 수는 없는 법, 간혹가다가 회사에서 혹은 개인 프로젝트를 할 때면 어김없이 프론트 특히 디자인을 구현하는 부분이 생기기 마련이다.

 

그런데 요즘 뜨는 바이브 코딩 중, Cursor를 통해 Figma의 디자인 링크만 넣어주면 자동으로 구현해준다는 것을 보고 심장이 떨려왔다.

드디어 재미없었던 화면 구현을 AI에게 맡길 수 있다고 생각했다.

 

그러면서 바로 관련 문서를 찾아보았고 MCP 를 통해서 Figma 와 Cursor 를 연결하는 것이었는데 아래 링크를 통해서 확인이 가능하다.

https://github.com/GLips/Figma-Context-MCP?tab=readme-ov-file

 

# git clone 
git clone https://github.com/GLips/Figma-Context-MCP.git

cd Figma-Context-MCP

# settings > security > personal access tokens 에서 generate token(write permssion 을 주면 된다)

vi .env 
FIGMA_API_KEY=<Figma 에서 얻은 키 입력>

# 그리고 저장

# brew 로 pnpm 설치(설치 되어있거나 os 가 다른 경우 맞춰서 설치 진행)
brew install pnpm

# 의존성 설치
pnpm install

#mcp 서버 실행 --figma-api-key 같은 경우에는 
#이미 .env 에서 설정해서 안해도 될 것 같으나 일단 대부분의 블로그에서 사용하고 있기에 포함
npx figma-developer-mcp --figma-api-key=<Figma 에서 얻은 키 입력>

 

위의 과정을 통하면 일단 mcp server 와 figma 는 연결이 되어진 것이다.

 

그리고 해야하는 것은 mcp server 와 cursor 를 연결해줘야 한다.

아래를 따라가면 가능하다.

# cursor 실행

# preferences > cursor settings > tools & integrations > mcp tools > new mcp server

# json 형식
{
  "mcpServers": {
    "figma-developer-mcp": {
      "url": "http://localhost:3333/sse",
      "env": {
        "API_KEY": <Figma 에서 발급받은 키>
      }
    }
  }
}

url 의 경우 현재 로컬로 띄워져있고 기본 .env 를 사용했으면 저 url 이 기본형태이닌 복붙해서 사용하고 API_KEY 만 바꿔주면 된다.

 

그렇게 한 이후에 피그마에서 구현이 필요한 부분을 아래와 같이 copy as > copy link to selection 으로 해서 링크를 복사하고
cursor 에게 해당하는 링크를 붙여주면서 프롬프르로 똑같이 구현해달라고 하면 mcp 를 통해서 소스를 가져와 구현을 시작한다.

이제 솔직히 써본 입장에서 말해보면

 

장점

1. 디자인 구현에 대한 리소스가 많이 줄어든 것은 사실이다.
2. 더욱 더 많은 시도를 통해서 더 나은 결과를 얻기위해 시도할 수 있다.

 

단점

1. 아직은 답답한 부분이 존재한다. -> 디자인 자체가 의도한 형태로 안나오는 경우가 빈번하게 존재

2. 코드 퀄리티가 떨어지는 부분이 존재한다. -> 리팩토링 필수 불가결

3. Cursor 에 대한 부분이 유료 Plan 이 필수적이다.

 

일단 이렇게 나오는데

 

만약에 그래서 사용할거야? 안할거야? 라고 말하면 무조건 사용할 것 같다.

 

나같은 경우에는 디자인 구현을 그렇게 좋아하지 않다보니 일단 시킬 것 같고 세부적인 부분만 내 손으로 구현하거나 리팩토링 할 것 같다.

물론 나머지 서비스 영역에 관련된 코드들은 검수가 무조건 필수이다보니 내가 대부분을 구현하는 형태일 것이다.

 

시간이 나면 GeekNews를 보는 버릇이 있다.

 

오늘도 잠깐 시간이 나서 글들을 보는데 "무엇이 강한 엔지니어를 강하게 만드는가?" 이것을 보고 흥미가 생겨서 클릭해서 보게되었다.

https://www.seangoedecke.com/what-makes-strong-engineers-strong/

 

What makes strong engineers strong?

Self-belief, pragmatism, speed, and technical ability

www.seangoedecke.com

관심이 있으신 분들은 한번 들어가서 봐보시기를 만약에 영어가 아닌 한글로 번역 및 요약이 되어진 것이 필요하면 아래링크로 들어가시면 됩니다.

https://news.hada.io/topic?id=21480

 

무엇이 강한 엔지니어를 강하게 만드는가 | GeekNews

강한 엔지니어는 약한 엔지니어가 아무리 시간과 노력을 들여도 할 수 없는 문제를 해결할 수 있는 사람임핵심 역량: 자기 신뢰, 실용주의, 속도, 기술적 역량Self-belief: 자기 신뢰강한 엔지니어

news.hada.io

 

일단 보고나서 느꼈던 것은 나랑 사고관이 비슷한 부분이 많다는 것이었다.

 

가장 크게 와닿았던 것은 "강한 엔지니어는 실제로 동작하는 결과물을 내는데 집중함" 이었다.

 

보통 새로운 프로젝트 혹은 아예 새로운 도메인, 기술 등을 시도를 할 때 완벽하게 모든 것을 학습하거나 혹은 어느정도 기준치가 되어야지만 시작하는 사람들이 있다.

하지만 나는 예전부터 그러한 형태로 개발을 많이 하지는 않았다.

 

중간중간 시간이 날 때면 따로 책을 읽거나 혹은 글을 통해서 공부를 하면서 지식을 틈틈히 쌓기는 했지만 무엇인가를 시도할 때에는 그렇게 많은 시간을 필요로 하지는 않았다.

오히려 시도를 통해서 헤딩하면서 문제를 해결하고 결과물을 만드는데에 초점을 맞추었다.

그러면서 어느정도의 퀄리티는 준수하려고 했지만 엄청나게 잘 짜여진 설계 혹은 코드 품질에 대한 초점을 맞추지는 않았다.

 

언제나 1순위는 돌아가냐였고 그 다음 순위들이 설계 혹은 코드 퀄리티였다.

 

그래서 보통 나의 목표가 돌아가는 것이 우선이다보니 일단 만들고 나중에 리팩토링하는 형태의 일을 주로 진행을 해왔다.

그러다보니 장점아닌 장점이 레거시 코드를 개선할 때 도움이 되었던 부분이 있었고

또 빠르게 결과물이 나오다보니 태핑을 시도하거나 결과물을 기준으로 피드백을 통하여 어떻게 나아가야 하는지에 대한 지표로 삼을 수 있었다.

 

최근 AI 관련 프로젝트를 할 때도 그랬다.

반려동물의 얼굴, 눈, 코 를 검출해야 하고 또 특징을 뽑아내 추후 다시 한번 들어올 때 유사도를 비교해야하는 부분이 있었다.

그때 얼굴은 어떻게 검출할 지에 대해서 확인해보니 YOLO 라는 부분이 있었고 그것을 학습하고 어떻게 실사용이 가능한지에 대해서 학습을 통해서 바로 실무에 적용을 하고 검출이 되어지는 것을 확인했다.

그리고 또, Embedding Model 흔히 특징을 검출해내는 모델에 대해서도 학습이 일단은 많이 되어진 것과 ViT 기반이 대세라는 것을 그냥 들었기에 괜찮겠지? 라는 생각으로 일단 시도를 했다. 그렇게 Embedding Model 을 통해서 특징을 뽑아내 유사도 체크를 했을 때 그래도 같은 개체에 대해서는 약 85% 에 대한 결과를 가져왔고 다른 개체인 경우에는 더 낮은 유사도인 0.5 정도의 유사도를 보여줬다.

물론 아주 유사하게 생긴 경우에는 그보다는 높은 유사도를 가져왔지만 일단 전이학습이 이뤄지지않은 상태에서는 나름 괜찮은 결과였다.

 

이렇게 무엇인가를 동작한다는 것에 초점을 맞추고 시작해 결과를 내었을 때, 무엇인가를 추가하거나 서비스적으로 이게 되어졌으면 하는 의견이 나왔기에 좋았던 결과였고 또 저 부분의 경우 모델의 성능이 좋아지면 결국에 프로그램의 퀄리티가 높아지는 것이기에 오히려 시도하는 것이 더욱 더 옳은 선택이었던 것 같다.

 

다시 글로 돌아와 무엇이 강한 엔지니어를 강하게 만드는가? 에 대해서 생각해보면

글에서는 자기 신뢰, 실행, 실용주의 적인 생각이 주를 이루었는데 나도 이 부분에 동의하고 있다.

그리고 스마트하지만 약한 엔지니어와의 갈등이 있을 수도 있다고 하는데 이부분도 어느정도 동의는 한다.

 

여기에서 약한 엔지니어라고 말하는 사람은 일단 내생각으로는 실행을 잘 안하는 사람이 될 것이다.

그러다보니 이론으로만 가지고 있어서 실제로 경험하는 부분이 매우 적어 업무에서 이렇게 이렇게 하면 되는거 아니야라고 하는 사람일 것 이다.

 

그런데 실제로 업무를 해보면 그게 아님을 안다.

인력, 시간, 자원 이러한 부분들이 이론과는 매우 차이가 나고 결국에는 시간내에 돌아가는 결과물이 나와야하는 것이 가장 큰 목표인데 스마트하고 약한 엔지니어는 그러지 않을 것이기 때문이다.

 

그래서 오히려 업무를 시작하기전에 혹은 업무를 하는 중에 태클을 주로 걸지않나 생각한다.

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

 

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

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)인지라 더더욱 힘들 것이라고 판단을 했기 때문이다.

서비스에서 수요성이 있는 정보에 대해서 PUSH를 보내면 사용자가 몰리고 그로인해서 서비스가 전체적으로 느려지는 현상이 발생을 한다.

그러면 어떻게 해야지 서비스가 느려지는 상황을 막을 수 있을까?

 

1. 캐시 사용

A. DB Query 관련

사용자가 그렇게 많지 않고 질의비용이 그렇게 비싸지 않으면 그냥 나름대로 그렇게 써도 문제가 없을 것 같다.

그러나 사용자가 많아질 경우 Connection이 많아지고 그로인해서 CPU 사용률이 갑자기 오르게 될 것이다.

그럴 때에는 간단한 질의에 경우에도 느린 결과값을 보여줄 것이고 심할 경우에는 Timeout이 발생할 확률도 크다.

그렇기에 Cache를 통해서 DB로 접근할 것들을 우회해서 Cache를 통해서 대응하는 것이 유효한 방법이 되어질 수도 있다.

 

B. View 관련

DB에 질의하는 것 이외에 Service를 실행하는 주체에 Cpu가 급격하게 치솟을 수도 있다.

트래픽이 크게 발생하여 그럴수도 있겠지만 해당 서비스에 대한 비용이 커서 CPU가 치솟았을 확률이 크다.

그렇기에 캐시를 할 수 있는 경우 해당 결과값을 캐시를 통해서 빠르게 전달만 하는 것이 효과적인 방법이 되어질 수 있다.

 

2. DB Index 설정

이상하게 그렇게 무거운 작업도 아닌데 오래 걸리는 경우가 있다.

모니터링 툴을 통해서 확인하면 보통 100ms 미만으로 떨어지는 쿼리가 있는 반면 비정상적으로 500ms 이상으로 나오는 경우가 있다.

이럴때 별다른 거 설정할 것 없이 인덱스만 설정해줘도 100ms 으로 떨어지는 경우가 대다수이다.

그리고 저렇게 느린 쿼리들이 많이 호출되는 경우 당연히 DB에는 부하가 생기기에 다른 작업에 대해서도 느려지고 이런 경우
전체적인 서비스 장애가 발생할 수 있다.

 

3. 서비스별 적절한 환경 사용

A. 대량의 트래픽을 예상하고 결과값이 다른 경우

이러한 경우 물리적인 서버대수를 늘리는 것도 방법이 되어질 수 있고 Auto Scale 로 대응을 하는 방식도 있지만

그렇게 대응하는 경우 예상치 못하는 비용의 발생 및 대처가 느리다라는 단점이 있다.

그래서 이때 유용하게 사용할 수 있는 것이 Lambda 와 같은 Serverless 기반의 환경인데 Lambda 를 기준으로 설명하면

Lambda 의 경우 트래픽에 따라 자동으로 대응을 하는 환경이고 Instance 의 Scale Out 대비 또, Request 별로 비용이 발생하다보니 경제적이다.

다만, Lambda 의 경우 Connection 이 직접적으로 DB에 붙는 경우 부하가 생길 수가 있기에 이러한 부분들은 상황에 따라서 우회하는 방향을 선택해야한다.

 

B. 대량의 트래픽을 예상하지만 결과값이 같은 경우

이러한 경우에는 결과값을 정적파일로 서빙을 하는 것이 괜찮다.

만약 내가 상품정보를 제공한다 했을 때 여러 부분은 정적 파일로 제공하는 것이 가능하다.
(내가 생각했을 때, 재고관련 제외하고 대부분은 가능할 것 같다.)

그리고 admin 상에서 상품에 대한 정보가 바뀔 때 해당하는 Response를 미리 정적 파일로 제공을 해서 클라이언트에서 요청시에는 정적파일을 요청하도록 협의가 되어진다면 많은 트래픽을 서비스에서 감당하는 것이 아닌 다른 환경을 통해서 해결할 수 있을 것이다.

 

 

그외 

정적파일의 경우 CDN을 통해서 서빙하는 경우 더욱 더 빠르게 제공을 해줄수도 있고,

해당하는 작업이 즉각적인 응답을 필요로 하지않는다면 비동기로 처리해서 할 수도 있다.(가입 이메일 등등...)

웹서버(Nginx, Apache 등등..) 에서 캐시를 태우고 LB에서 Sticky 설정을 통해서 캐시 대응 및 instance memory 캐시 설정,

Auto Scale 설정을 통해서 비정상적인 상황에 자동으로 대응하도록 설정,

레거시 코드 개선 등등 이 있을 수 있다.

 

 

 

 

 

회사를 다니면서 겪었던 이슈 중 가장 난해했던 이슈였다.

문제 발생

회사를 옮기고 해당 회사에서 어느정도 도메인 및 서비스에 대해 익어갈 때쯤 겪었던 이슈였는데

슬랙에서 보통 에러관련해서 메시지가 전사채널에 오는데 이거하고 관련된 이슈면 대부분 메시지 내용은 아래와 같았다.

  • 이미지가 엑박으로 떠요
  • 배경 이미지가 삭제가 안되어져요

그러면 S3에 들어가 해당 object를 확인해보면 분명히 확장자는 이미지인데 MetaData 에 있는 Content-Type이 이상하게 들어가있었다.

  • application/json
  • text/javascript

 

그렇게 되어지니 CloudFront 를 통해서 Lambda 가 이미지에 대해 변형을 시도할 때 에러가 발생해서 엑박이 뜨거나
다른 외부 API 를 통해서 배경을 삭제하려고 하는데 에러가 발생하는 경우 이렇게 두개의 건이 많았다.

 

시도

일단 해당 오류는 잡기가 너무 어려웠다.

코드가 순수하게 django 에서 지원하는 python에서 지원하는 것만 사용되어지다보니 알 수가 없었다.

models.ImageField() # 이것만 사용

그리고 계속적으로 발생하는 것이 아닌 어쩌다가 발생하는 경우이고 또 발생한다해도 이미 이미지 파일은 예전에 올라가서 에러 시기를 잡을 수 없다는 거였다.

 

그래서 파일의 content-type 관련해서 연관있는 코드를 확인해보니 따로 수동적으로 세팅이 되어진 부분은 없었고

순순 python 과 boto를 통해서 돌아가는 것을 알 수 있었다.

 

그래서 혹시 이부분이 python 과 boto 가 추후 업데이트하면서 바뀌었던 포인트가 있었을까? 하고 찾아보니 있었고 혹시 그러면 이부분은 업데이트만 하면 해결이 되어질 수도 있다고 생각했다.

 

약 해당 오류를 접하고 7개월 정도만에 업데이트를 진행했고 해결이 되어진 줄 알았었다.

왜냐하면 오류관련 메시지가 오지 않았기 때문이다.

 

다시 발생하는 에러

그러다가 재택으로 근무하는 도중 이미지의 배경이 삭제가 안되어지고 해당 이미지를 보니 엑박으로 떠요라는 메시지가 개인 DM 으로 왔었다.

 

그래서 순간의 감정은 !!!!!! 이거였고 해결된 거 아니였었나... 하면서 속으로 생각을 했다.

그러면서 에이 그래도 이미지 재업로드하면 문제 없겠지 하면서 호기롭게 제가 한번 다시 이미지 업로드 할게요
그러면 일단은 해결되어질 것 같아요 하면서 DM을 보냈고 이미지를 업로드하고 해결이 되어질 줄 알았다.

 

이미지를 올리고 S3에서 Contetn-Type을 확인해보니 무슨 이런일이... application/json으로 올라가는 것이었다.

계속 올려도 이것은 동일했다.

 

그러면서 이거는 어!!! 문제다라고 생각했고 도대체 어디에서 문제가 발생하는 거지? 하면서 등줄기에 식은 땀이 흘렀다.

 

기시감

그러면서 생각했다. application/json 이라는 Content-Type 왜 이렇게 기시감이 들지? 하면서 머릿속을 곰곰히 생각해보니 딱 떠오르는 하나가 있었다.

 

백오피스에서 내가 하나의 파일을 올린 것이 있는데 그 파일의 Content-Type을 application/json 으로 설정했는데 혹시??? 하면서
그 백오피스에서 한번 Content-Type 을 이미지로 설정하고 업로드 후 다시 제품 image 파일을 넣었는데 갑자기 정상동작을 하기 시작했다.

 

그때 깨달았다 여기 관련된 코드가 무언가가 잘 못되어서 전체적으로 백오피스에서 이미지를 올릴 때 이슈가 생기는 구나 하고 말이다.

 

원인

아래의 코드가 문제가 있었던 코드다.

# admin.py
options_dict = {
	"CacheControl": self.cleaned_data.get("cache_control"),
	"ContentType": self.cleaned_data.get("contents_type"),
}
self.instance.upload_file.storage.object_parameters.update(options_dict)

 

보면 form을 통해서 받은 데이터를 storage 의 object_parameters 에 update를 한다.

참고로 저 storage는 django-storages 에 있는 s3가 사용하는 부분이다.(s3 기반으로 사용하고있어서)

 

그러면 저렇게 update를 하면 어떻게 동작을 하는지는 내부 라이브러리 코드를 보면 알 수 있는데

# django-storage 에 있는 s3.py
class S3Storage(CompressStorageMixin, BaseStorage):
	...
    def get_default_settings(self):
        return {
            ...
            "object_parameters": setting("AWS_S3_OBJECT_PARAMETERS", {}),
        }
        
    def _get_write_parameters(self, name, content=None):
        params = self.get_object_parameters(name)

        if "ContentType" not in params:
            _type, encoding = mimetypes.guess_type(name)
            content_type = getattr(content, "content_type", None)
            content_type = content_type or _type or self.default_content_type

            params["ContentType"] = content_type
            if encoding:
                params["ContentEncoding"] = encoding

        if "ACL" not in params and self.default_acl:
            params["ACL"] = self.default_acl

        return params

 

 

저기 보면 object_parameter 의 default 값은 빈 dict이다. 그러면 대부분은 _get_write_parameters 에서 python 이 제공하는 mimetypes.guess_type 을 통해서 올바른 content-type 이 설정되어진다.

 

그런데 object_parameter 를 업데이트를 하고 심지어 ContentType이라는 key 가 설정되어지면 저기에 있는 순수하게 돌아가야하는 부분들이 정상적으로 동작을 안하고 object_parameter 에 값으로만 돌아간다.

 

저기 같은 경우는 서버가 다시 재시동을 하거나 혹은 어쩌다 우연히 이미지 파일로 올라갈 때만 정상적으로 동작하는 것처럼 보이게 되어지는 것이다.

 

해결

일단 해결은 간단했다.

저 부분을 없앴고 ContentType, CacheControl 같은 경우는 따로 파일이 올라간 이후 수정을 자동적으로 하는 형태로 변경해서 작업을 진행했다.

 

그러면서 입사하기 이전부터 오랫동안 유서 깊게 발생하던 오류를 수정하는 순간이었다.

 

후기

해당 오류는 솔직히 개인적으로 너무 찾기 힘든 오류였다.

 

이유는 

  • 내가 입사하기 이전에 작성되어진 코드
  • 전역적으로 관리하는 settings 가 아닌 다른 쪽에서 의도치 않게 사용되어진 부분
  • 배포 혹은 다른 파일을 해당 경로로 올릴 경우 발생을 안한다.
  • 문제를 겪는 사용자와 인스턴스가 겹치지 않는 경우 해당 에러를 접할 수 없다.
  • 간혈적으로 발생하는 이슈
  • 테스트 코드 없음

이러한 부분이 정말로 힘들게 했고 진짜 우연히도 해당 오류를 제보를 받음과 동시에 이전에 내가 application/json 파일을 올린 것과 같은 instance 에 접속을 하고 있었던 것이 겹쳐서 해당 오류를 해결할 수 있었다.

 

아니었으면 해결하는데 엄청 오래걸리지 않았을까 생각한다.

python 3.8 의 생명이 별로 남지않았다.

 

일전 다른 글에서 적었듯이 회사의 프로젝트 중 python 버전을 3.8로 사용하고 있었다.

그래서 해당 기간이 끝나기전에 버전을 올릴 필요가 있었다.

 

그러면 버전을 왜 올려야하나?

솔직히 말하면 지금 잘 돌아가는데 굳이 바꿀 필요가 없을수도 있다.

그런데 왜 바꿔야 하는건가? 라는 질문에 내 생각을 나타내면 아래와 같다.

 

1. 성능적인 이점

python 3.9, python 3.10 버전에 대해서는 따로 성능적인 업데이트에 대한 내용이 없다.

하지만 3.11 에서는 성능이 전작기준 대비 평균적으로 약 25% 상승이 있었다고 말한다.

그렇다면 일단 전체적으로 코드의 큰 변경없이 서비스의 속도가 향상되는 것을 기대할 수 있으니 큰 이점으로 다가온다.

 

2. 생산성 향상

나는 주로 python3.11 혹은 python3.12 를 사용해왔다(그전에는 주로 3.6....).

딱히 이유가 있어서가 아닌 그때 당시에 release가 되어진 버전을 사용했다고 보는게 맞는데 이러한 버전을 쓰다가

현재 회사프로젝트가 python3.8 을 사용하다보니 약간의 생산성이 떨어지는 부분이 생겼다.

 

사소하게는 typing 관련해서 예시를 들 수 있다.

#python 3.8 version
import typing

def test_typing(list_item: typing.List[str]):
	for item in list_item:
    	print(isinstance(str, item))
        

#python 3.11 version
def test_typing(list_item: list[str]):
	for item in list_item:
    	print(isinstance(str, item))

 

 

어떻게 보면 사소하다고 할 수 있는데 저러한 코드를 계속 작성하다보면 저 사소한게 의외로 크다.

 

주로 버전별로 업데이트 사항이 축약되어져서 나오기는 하는데 3.8에서 3.11 더 나아가 3.12까지 봤을 때

python 에서 type 관련으로 많은 업데이트가 있었다.

이러한 부분이 어떻게 보면 생산성 증대가 되어진 부분도 있다.

 

3. 라이브러리의 노후화 및 지원을 받지 못한다.

라이브러리도 결국은 지원하는 python 버전이 있고 대부분 지원 종료가 끝나 python version 의 경우에는 지원을 안하게 된다.

 

이유는 당연히 그 python 버전을 지원하기 위해서 따로 코드를 더 작성해줘야 하고 분기를 태워야하는데 그게 말로만 들어도 어려운데

실제로 하려고 하면 더 어렵다.

 

심지어 지원이 종료된 경우에는 또 찾지 못한 버그가 있을 수도 있는데 그것들로 인해서 악용이 되어질 수도 있다.

 

만약 본인이 쓰고있는 python version 이 지원이 종료가 되었다. 그렇다면 흔히 쓰는 클라우드 컴퓨팅 제공 업체에서 지원을 안하는 경우가 생길 수도 있다. 기본적인 방법이 아닌 작업의 공수가 커진다는 것을 의미한다.

 

결국 deprecated 가 되어진 버전으로만 작업을 해야하는 순간이 올 수도 있는것이다.

 

버전을 올리면서 겪은 이슈

1. pandas 설치 이슈

일단 이상하게도 버전을 업데이트하고 라이브러리를 새롭게 설치를 받았는데 너무나도 느렸던 경험이 있었다.

하지만 한번 설치한 이후에는 그거를 다시 설치를 하지않기에 몰랐는데

배포를 하는 경우에는 대부분 새롭게 설치를 하는 경우가 기본이기에 거기서 문제가 발생했다.

AWS의 EB를 통해서 배포를 하는데 배포가 완료되기까지 너무 시간이 오래걸리거나 혹은 안되는 경우가 발생을 하는 것이었다.

 

그래서 열심히 무엇이 문제일까 찾아보다가 log를 확인해보니 오래 걸리는 라이브러리를 찾을 수 있었고 pandas 에서 문제가 발생한다는 것을 알았다.

 

그래서 구글링을 해보니 python 버전마다 pandas의 버전이 잘 호환이 되어지는 버전이 존재하고 그 버전으로 라이브러리를 설치해야지 설치에 대한 이슈가 없다는 것이었다.

그래서 당시 python 3.8, pandas 1.4.3 -> python 3.11, pandas 2.2.2 로 올려서 설치를 하니 해당 이슈가 해결되었다.

 

2. python 3.11 에서 drf-yasg -> drf-spectacular 변경

회사 프로젝트에서 문서화 라이브러리를 drf-yasg를 사용하고 있었다.

그런데 해당 라이브러리가 python 3.11을 확실히 지원한다는 문구를 찾을 수도 없었고

또한 github 을 확인했을 때 최근 commit 내역이 없어서 이거는 변경의 필요성이 있다고 판단했다.

그래서 찾아보니 drf-spectacular 는 일단 commit 내역이 최근에도 있으며 지원을 한다고 명시가 되어있었다.

 

그리고 더불어 pydantic 외 여러가지 지원하는 요소가 많았기에 문서화 라이브러릴 바꿀 가치가 있다고 판단했다.

물론, 옮기는 것이 쉬운 작업은 아니었다. 너무 많은 API 가 있었기에 해당하는 부분을 하나하나씩 이전 문서를 보면서 바꾸는 것은 쉽지않았다.

 

3. Cache 관련 문제

라이브러리 업데이트 하면서 캐시 관련으로 쓰는 django-cacheops 와 redis 를 업데이트 했다.

 

그런데 이슈가 생긴 부분이 기존 django-cacheops 에서 사용하던 cacheops.redis.CacheopsRedis 가 redis.Redis 로 변경되어져서 사용해야 했고 그러다보니 기존에 설정해놓은 DegradeRedisClient 가 정상적으로 동작을 안하게 되는 경우가 생겼다.

 

원래 시나리오의 경우

redis 의 접속이 안되어진 경우 db를 통해서 데이터를 가져와야하는데 이 부분이 문제가 생겨서 redis 의 접속이 안되어진 경우 그냥 에러를 발생시키는 것이었다.

 

그래서 해당하는 부분을 수정할 필요성이 생겼다.

 

일단 처음에는 타임아웃이 0.5초 정도로 설정되어 있었고 이부분에서 간혈적으로 타임아웃 이슈가 발생하여 대응이 안되어지는 줄 알고

따로 Timeout 을 exception 처리를 했다.

하지만 이렇게 하더라도 문제가 발생하는 것이 timeout 외 이슈가 발생할 요지는 너무나도 다분했다. 잠깐이라도 운영상에서 cache 가 제대로 정상동작을 안하는 상황이 발생하면 전체적인 서비스 장애가 발생하니까 말이다.

 

그래서 최종적으로는 모든 부분에 exception 처리를 했고 redis 에서 버전을 체크하는 부분만 따로 작성해두어서 그 케이스만 통과하도록 변경했다.

 

class DegradeRedisClient(Redis):
    def execute_command(self, *args, **options):
        try:
            return super().execute_command(*args, **options)
        except Exception:
            if args[0] == "INFO" and args[1] == "server":
                return {"redis_version": "0.0"}
            return None

 

이렇게 된 경우 모든 exception 에 대해서 문제가 안생기고 따로 redis에서 예전 버전을 지원하기 위해 작성되 version 확인 스크립트는 연결이 되어지지 않은 상황에서도 문제 없이 넘어간다.

 

후기

약간 급하게 마무리 하는 감이 없지 않아있다.

그거는 이 버전업을 진행할 때 혼자 진행을 하다보니 모든 케이스에 대해서 알고는 있는데

너무 자잘하게 뭐가 deprecated 가 되었으니 이거 사용하세요 보다는 그냥 시간이 가장 많이 소요되었거나 중요도가 있는 부분을 다루다보니 그렇게 되었다.

 

일단 후기를 말하면 이렇게 업데이트를 하게 되면서 좋은 점은

 

해당 프로젝트에서 test code를 처음으로 작성했다는 것이다. 이전에는 test 라이브러리 및 코드조차가 없었다... 그러다보니 배포할 때마다 살이 떨렸는데 지금은 조금 괜찮아졌다.... 왜냐하면 전체 테스트를 작성하기에는 작업양이 어마무시하기에 엄두가 아직은 안난다.

 

두번째는 pydantic을 사용한다는 것이다. dataclass 가 나쁜 것은 아닌데 pydantic 이 여러모로 간편하기도 하고 AI 및 통계에서 말하는 성능에 대한 이점을 크게 높여야 하는 것이 아니면 dataclass 보다는 pydantic 을 사용하는 것이 생산성이 높다고 판단하기에 아주 좋았다. 기존에 python3.11 을 쓰다가 python 3.8을 쓸 때 이부분이 약간의 족쇄로 다가온 부분이 없지않아 있었다.

 

세번째는 성능이 확실히 좋아졌다. 이게 뭔가 지표적으로 보여줄 수는 없지만 현재에도 개선을 계속 진행을 하고 있다보니 기록은 있어도 불확실하다.

하지만 그때 당시 업데이트 하기 이전과 후를 비교했을 때 약 20% 정도는 빨라진 것으로 보였다. 그러다보니 사용자가 느끼는 편의성은 더 좋아지지 않았을까 생각한다.

 

 

Django의 3.2LTS 버전이 2024년 4월에 끝나게 되었다.

 

회사 프로젝트가 django 3.2.12 를 사용하고 있었는데 당시 Readme 에 내가 적어놓은 프로젝트 환경이다.

굵은 글씨로 업데이트 필요라고 적어놓았는데 django 는 해당하는 시간이 지나고 나서 업데이트가 되었고

python 같은 경우는 그래도 지원 종료 시기보다는 그전에 업데이트를 완료했다.

 

해당 글은 그중에서 django 3.2 에서 4.2 로 변경하면서 겪었던 것들을 적으려고 한다.

 

겪었던 항목(자잘한 것 제외)

1. Django url Deprecated

Django 4.0 remove feature 여기 글을 보면 알 수 있는데 django.conf.urls.url 이 사라진 것을 알 수 있다.

 

Django 4.0 release notes | Django documentation

The web framework for perfectionists with deadlines.

docs.djangoproject.com

회사 프로젝트의 경우 url을 기본전제로 사용하고 있었고 당연히 3.2 에서 4.2로 업데이트를 하자마자 ide 에서 무수한 빨간 밑줄이 발견되었다.

뭐 다행히 url => re_path 로 변경했다.

 

참고로 django-restframework 를 사용하고 있고 거기에 있는 router 를 사용하고 있다면 여기에 대해서 이슈사항은 없을 것 같다.

django-restframework 를 확인해보니 내부적으로 re_path 를 이미 사용하고 있었고 회사 프로젝트가 특이해서 router 같은 거는 일체 안쓰고 오직 urls 로만 모든 경로를 설정했기에 변경하는데 이슈가 컸었다.

 

2. Django order by 에 있는 `model.id` 형식

이 부분은 많은 사람들이 안 겪을 것 같은데 일단 회사 프로젝트가 여러 사람의 손을 거치기도 했고 python과 특히 django 를 주로 했던 사람들이 처음에 작업했던 것이 아니다보니 예전 코드들을 보면 신기한 부분들이 많다.

이것도 그중에 하나인데 이전 버전 같은 경우에 `model.id` 이러한 방식으로 한다고 해서 문제는 일어나지 않았던 것 같다.

하지만 버전이 올라감에 따라서 validation 이 되어진 것인지 releated_query_name 으로 해야지만 오류가 발생하지 않는다.

 

그래서 약간의 궁금점이 들었다. 그러면 옛날 방식으로 했을 때 정렬이 정상적으로 동작한 것 인가?

https://stackoverflow.com/questions/2065863/django-order-by-related-field

 

Django order by related field

I want to sort a QuerySet of contacts by a related field. But I do not know how. I tried it like this, but it does not work. foundContacts.order_by("classification.kam") Actually in a template I ...

stackoverflow.com

위의 글을 보면 정상동작은 하지 않았던 것으로 보인다. 아마 에러만 안나서 정상동작의 여부에 대해서 판단을 하지 못했던 것으로 보인다.

 

그래서 해당방식을 아래와 같이 변경했다.

# 이전 방식
.order_by('product_content.id')

# 변경
.order_by('product_contents_set__id')

 

3. ugettext_lazy Deprecated

ugettext_lazy 의 경우 django 에서 python2를 지원하기 위해서 만들어진 함수인데 django 가 더이상 python2 를 지원하지 않기에 자연스럽게 Deprecated 되었다.

 

문제는 우리 프로젝트는 한번도 python2 였던 적이 없다...

 

이말은 그냥 gettext_lazy를 사용했어도 되는 부분이다.

python2에서는 따로 유니코드 문자열을 처리해줘야하기에 사용하지만 python3은 그럴 필요가 없기 때문이다.

 

그래서 해당하는 부분을 변경해주고 나니 이슈는 없었다.

 

 

번외

 

해당 프로젝트를 진행하면서 pillow 도 버전을 업데이트 했는데 이때 발생했던 이슈가 총 두가지였다.

 

1. ImageDraw.Draw 의 textsize deprecated

기본적으로 user 의 기본 프로필 이미지를 생성할 때 닉네임의 앞 두글자를 따서 프로필 이미지를 만드는데 그때 쓰는 로직중

textsize 를 사용하고 있었다.

 

그런데 저 부분이 동작을 제대로 안하고 있었기에 확인해보니

textsize 는 deprecated 되었고 대신 textbbox를 사용하라고 되어있었다.

 

그래서 아래와 같이 변경되었는데

# 이전 방식
w, h = draw.textsize(msg, font=font)

# 변경
_, _, w, h = draw.textbbox((0.0), msg, font=font) # return left, top, right, bottom

 

참고로 right 를 w, bottom 을 h 로 한것은 x, y 0,0 의 기준점이 상단 왼쪽이기 때문에 width 와 height 의 기준이 맞기에 저렇게 했다.

 

2. avif 파일 미지원

이 부분은 운영상에서 발생했는데

어드민에서 이미지를 올리려고 하니 avif 파일이 안올라간다는 이슈였다.

pillow 에서 지원을 하지 않기에 따로 라이브러리를 추가해서 사용하고 있었는데 그때 사용했던 라이브러리가 pillow-avif-plugin 이 었는데 이것도 1.3.1 을 사용하고 있었고 올라간 pillow 와는 대응이 안되어지는 버전이었던 것 같다.

 

그래서 해당 라이브러리만 1.4.3 으로 업데이트 해주니 이슈는 해결이 되었다.

 

후기

이렇게 한번 업데이트를 진행해보니 크게 느꼈던 것은 테스트코드가 최소한만으로 있었어도 업데이트가 쉬웠을텐데라는 생각이였다.

 

참고로 API 만 해도 셀 수 없이 있는데 그거를 하나하나 케이스별로 테스트를 손수했다. ㅠㅠ

이유는 테스트코드가 없었기에 보장을 하려면 결국 모든 케이스를 손수 테스트해야했기 때문이다.

 

그래서 이러한 부분이 매우 아쉬웠고 또, 하나는 이 부분을 너무 급하게 하지 않았나라는 생각이다.

 

시간의 여유를 두고 이전의 계획을 세워두고 했으면 좋았을텐데 다른 프로젝트의 연관성 때문에 버전업을 너무 급하게 한 경황이 없지않아 있었다.

 

그래서 오히려 시간을 두고 지원 시간이 끝나기전에 작업을 했었으면 좋았을 것이라는 사견이 있다.

 

지원시간이 끝나고 했기에 이미 늦은 시간이였다고 판단이 되어지기 때문이다.

 

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

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

 

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

 

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

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

 

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

 

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

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

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

 

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

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

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

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

 

이렇게 3개가 있다.

+ Recent posts