오늘 회사에서 다른 사람들로 부터 기능요청을 받았다. 원래 기획자 같은 role 의 사람이 있었지만 너무 바쁜지라(작은회사는 멀티role 이 당연한지라) 내가 직접 받게 되었다. 그런다 보니 원래 의도를 알 수 없는 UI 수정 요청사항을 받게 되었다. 그러다 이런 식으로 요청하면 아무래도 개발자가 방어적으로 될 수 밖에 없겠구나 하는 생각이 들었다. 

 기능 요청을 받다보면 불필요한 요청이나 불가능하거나 매우 구현이 어려운 요청을 받게 되어 개발자가 방어적으로 변하게 되는 것 같다. (여기서 불필요한 요청이란 딱 한번 하나의 데이터가 필요한데 있으면 좋겠다는 생각이 들어 기능으로 데이터를 구현해 달라거나 그냥 지레짐작으로 사용자에게 필요하겠지라는 생각으로 만들어 달라고 하는 요청이다. )
 그 외 또 개발자가 방어적으로 변한 이유 중 하나는 요청하는 사람이 개발자에게 충분히 생각할 시간을 주지않고 갑작스럽게 요청하게 되는 것도 하나의 이유 같다. 갑자기 요청자가 자리로 불러서 기능을 요청하는 경우, 요청자는 충분히 많이 생각했지만 개발자는 요청하는 사항에 대해 깊이 생각하지 못했을 때, 요청하는 사항에 대해 분명 이렇게 만든 히스토리가 있었던 것 같은데(기술적으로 어렵거나 다른 논리상의 충돌이 있거나 하는 등등) 그게 기억이 나지 않다보니 우선 방어하자라는 생각을 하게 되는 것 같다. 그리고 기술적 검토도 충분하지 않다보니 의외로 싶게 해결할 수도 있는데(해당 기능을 지원하는 요즘 핫한 library 가 있을 수도 있다.) 모르고 기술적으로 어렵다고 이야기 해버릴 수도 있다.  

그래서 이런 기능요청이나 버그을  위한 slack 채널이 따로 존재한다. 그래서 가능한 글로 미리 전달해 주고 추가사항 있다면 좀 나중에 자리로 불러서 설명해주면 좋은데, 왜 처음부터 생각할 시간을 안주는지 모르겠다. 생각할 시간이 충분하다면 내가 더 좋은 제안을 할 수도 있고 히스토리를 확인 해서 이유를 알 수도 있을텐데.

예제에는 개발하다가 의문이 생기면 MSDN 이라는 것을 이용해서 문서를 찾아야 했다. 이거 보통 CD 6장을 정도를  따로 설치해야 하는 Document 문서 검색기 같은 거다. 인터넷을 이용해서 검색할 수는 있었지만 별달리 도움이 되진 않았던 것 같았다. 
 그 후 winapi.co.kr  라는 사이트가 언제 부터 인지 자주 사용하게 되었다. 기본적인 API 를 검색할 수 있었다.
여기서 API 라고 했지만 그 시절 그냥 C 표준 라이브러리나 C++ 표준 라이브러리 함수 원형, 리턴값 등을 검색하거나 windows 개발을 위한 윈도우 전용 SDK 라이브러리 이용법등을 검색하는 수준이었다. 

그리고 회사에 다니고 부터는 Google 검색에 꽤 많이 의존했던 것 같다. 어느 순간 부터는 stackoverflow 라는 사이트가 구글 검색의 상단에 노출되기 시작했고 이 때 부터는 단순히 함수 자체를 물어보기 보다는 알고리즘, 로직, DB 쿼리 짜는 법, CSS 기술 등등 다양한 것을 물어 보면 꽤 자세히 찾을 수 있게 되었다. 한 번에 다 찾을 수 없었지만 중간중간 단계를 설정해서 여러개를 조합하면 내가 원하는 결과를 만들어 낼 수 있었다.(그러나 이상하게도 1, 2, 3  예제을 조합하면 이상하게 동작하지 않는 신기한 현상을 경험하기도 했다.) 만일 이래도 못 찾으면 내가 세상에서 이상한 것을 하는 사람인가 라는 의심도 하기도 했다. 

어느 순간 AI 시대가 되면서 AI 가 개발하는 시대로 접어든 것 같다. 아직 AI 개발은 발전 중이긴 하지만 AI를 이용한 검색은 확실히 많이 띄어난 것 같다. 

  어제 파이썬 requests 에 files 를 사용하면 어떤 HTTP Header 를 기본값으로 갖으면서 해당 서버로 request를 보내는지 확인하고 싶었다. 그래서 구글 검색을 통해 이리 저리 찾아 봤다. 쉽게 찾을 수 있을 지 않았지만 시간이 꽤 걸렸다. 

 그래서 Gemini 에게 물어 봤다.

## 질문
파이썬 requests 라이브러리에서 files 인자를 사용해서 파일을 업로드 하면 자동으로 multipart upload 방식으로 업로드는 되는 건가요? 기본적인 request header 가 어떻게 정의 되는지 궁금합니다.

## 답변
네, 파이썬 requests 라이브러리에서 files 인자를 사용하여 파일을 업로드하면 자동으로 multipart/form-data 방식으로 인코딩되어 업로드됩니다. 이는 웹 표준에 따른 파일 업로드 방식이며, 파일 데이터와 함께 다른 폼 데이터도 함께 전송할 수 있도록 설계되었습니다.

기본적인 Request Header 정의:

requests 라이브러리는 파일 업로드 시 다음과 같은 기본적인 request header를 자동으로 설정합니다. 특히 Content-Type 헤더가 multipart/form-data로 설정되는 것을 확인할 수 있습니다.

Content-Type:

파일을 업로드할 때 files 인자를 사용하면 requests는 Content-Type 헤더를 multipart/form-data로 설정합니다.

이 헤더 값에는 파일 데이터를 구분하고 각 부분의 정보를 담는 boundary 라는 고유 문자열이 포함됩니다. 예를 들어 다음과 같은 형태입니다.

Content-Type: multipart/form-data; boundary=----------------------------abcdefg12345
boundary는 각 폼 필드와 파일 데이터를 구분하는 역할을 하며, 서버는 이 boundary를 기준으로 요청 본문을 파싱하여 데이터를 추출합니다.

Content-Length:

요청 본문의 전체 크기를 나타냅니다. 파일 데이터와 다른 폼 데이터의 크기를 모두 포함합니다. requests는 파일 내용을 읽어 전체 크기를 계산하여 자동으로 설정합니다.
User-Agent:

requests를 사용하는 클라이언트 정보를 나타냅니다. 기본적으로 python-requests/{version} 형태로 설정됩니다. 필요하다면 headers 인자를 통해 사용자 정의 User-Agent를 설정할 수 있습니다.
Accept:

클라이언트가 이해할 수 있는 응답 미디어 타입을 서버에 알립니다. requests는 특별히 설정하지 않으면 */* (모든 타입)으로 설정될 수 있습니다. 서버가 특정 타입의 응답을 보내도록 기대한다면 headers 인자를 통해 설정할 수 있습니다.
기타 표준 HTTP 헤더:

요청 방식(GET, POST 등), 쿠키 (Cookie), 인증 정보 (Authorization) 등 다른 표준 HTTP 헤더들도 필요에 따라 자동으로 포함되거나 headers 인자를 통해 명시적으로 설정할 수 있습니다.

너무 자세히 알려주었다.

내 의문도 해소되었다. 

 

이젠 검색도 AI 를 이용해야 하는 시대가 되어 버렸다. 

나는 복잡한 SQL 쿼리를 사용해야 하는 경우, 쉽게 유지보수하기 위해 아래와 같이 with cte  구문을 개인적으로 많이 사용한다. 물론 실질적으로 Python 에서 sqlalchemy 을 사용하기는 하지만 아래와 같이 코드가 나오도록 하는 편이다. 

with 테이블1 as (
    select ....
),
테이블2 as (
    select ....
),
테이블3 as (
    select ...
)
select ...
    from 테이블4
    left join  테이블1 과 테이블4
    left join  테이블2 과 테이블4
    left join  테이블3 과 테이블4

이렇게 만들어 두면 뭔가 더 해석하기 쉬워지는 느낌이 있다. 물론 테이블1, 테이블2, 테이블3 이런 것 내에서도 내부적으로 left join 이 들어 있을 수 있다. 그리고 이렇게 하는 추가적인 이유는 테이블1, 테이블2, 테이블3 이 row 수가 적당해서 괜찮은 속도를 내 줄거라는 기대도 있는 편이다. 
그런데 오늘 where 조건이 걸렸을 때 늦어지는 현상을 발견했다. 원인을 분석해 봤는데 내 의도대로 테이블1, 테이블2, 테이블3 가 만들어지지 않고 Postgresql 알아서 최적화 하면서 다른 방법으로 동작해지면 속도가 늦어졌다. 

 그래서 이 문제는 google gemini 물었더니 materialized  를 사용하라는 답변을 받았다. (물론 gemini 가 추천해준  SQL구문이 틀렸지만 나에게 도움이 되는 답변이었다.) 이런 구문을 cte materialized  라고 하는 것 같다. 

with 테이블1 as MATERIALIZED (
    select ....
),
테이블2 as MATERIALIZED (
    select ....
),
테이블3 as MATERIALIZED (
    select ...
)
select ...
    from 테이블4
    left join  테이블1 과 테이블4
    left join  테이블2 과 테이블4
    left join  테이블3 과 테이블4

이렇게 해 주면 내 의도대로 임시로 테이블1, 테이블2, 테이블3 를 만든다고 한다. 


만일 sqlalchemy 를 사용한다면 다음 처럼 사용할 수 있다. 

s = select([orders.c.order]).cte("regional_sales")
s = s.prefix_with("MATERIALIZED", dialect="postgresql")