해당 소스에 중요한? 변화가 있다.
조건
- content-type : x-www-form-...
- method: post
- 호출을 받는쪽에서는 (스프링부트 3.x) @RequestBody ...
- url : xxxxx?a=b
- body : par1=val1.....
- 즉 쿼리스트링이나 body 는 전통적인 parameter 형태(k:v)
위의 상황에서 empty body 가 나옴.
내용 설명
x-www-form.. 은 전통적인 Parameter 형태이다. 즉 키:값의 구조이다. (method 를 논하는게 아니다) 쿼리스트링에 달려오는 파라미터또한 k:v 이다.
x-www-form 의 경우 request.getparam... 을 하게 되면 쿼리스트링말고도 바디까지 다 읽는다. 이건 j2ee(지금은 자카르타) 의 규약이다.
"The parameters data from the GET request query string and the POST request body are aggregated into the request parameter set."
(해석): "GET 요청의 쿼리 스트링 데이터와 POST 요청의 바디 데이터는 요청 파라미터 집합으로 '합쳐(집계)'진다."
제미나이가 찾아줌.
자.. 그럼 앞의 조건을 보자.
x-www-form... 형태로 던지면서 쿼리스트링에 파라미터가 달려 있다면... req.getPara.... 을 할때 바디까지다 읽어버린다(from stream.. inputstream)
우리는 한가지 기억할게 Inputstream 을 읽어버리면 1회성이므로 사라진다.
다행인건 이렇게 읽은거는 파싱해서(content-type 에 맞게) parameters 에 보관한다.
여기까지는 좋았다. inputstream 이 소비되었어도 Parameters 에 있으니까 그걸 꺼내면되거든. 근데 문제는... 스프링쪽의 소스가 변겨오디었다.
2.7 까지는 request 에서 읽어버린 parameters 를 잘 돌려줬다면, 3.0에서는 쿼리스트링이 있는경우 이미 소비된 inputstream 을 리턴하게 된다. 아래 소스를 보자. 왼쪽은 3.x 오른쪽은 2.x 에서 사용하는 스프링리소스이다. (스프링부트와 스프링은 별개다)

즉 3.x 로 오면서 스프링에서는 쿼리스트링이 없는 경우 기존에 파싱해놓은 parameters 를 그대로 리턴해주지만, 있다면? 소비되었을지도 모를 inputstream 을 그대로 돌려준다. 즉 request.getparam... 을 호출한 적이 있다면 Inputstream 은 이미 소비되어서 비어있으므로 empty 에러를 맞게 된다.
2.7 에서는 무조건 parameters 를 돌려준다.
그럼 왜 이렇게 바꾼걸까? 제미나이의 변호?? 는
왜 스프링은 코드를 이렇게 바꿨을까? (변호)
스프링 개발자들도 이유 없이 바꾼 건 아닙니다. 2.7 방식(무조건 복구)에는 치명적인 논리적 결함이 있었기 때문입니다.
- 2.7의 문제:
- 요청: POST /test?mode=debug (Body: name=spring)
- 2.7 동작: 파라미터 맵을 다 합쳐서 바디를 만듭니다.
- 복구된 바디: mode=debug&name=spring
- 왜곡: 원래 바디에는 name=spring만 있었는데, URL에 있던 mode=debug가 마치 바디 데이터인 것처럼 둔갑해서 @RequestBody에 들어갑니다. 이는 데이터 오염입니다.
- 3.0의 결정:
- "쿼리스트링이 있으면, 파라미터 맵에서 어디까지가 바디고 어디까지가 쿼리인지 완벽하게 분리할 수 없다."
- "부정확하게 섞인 바디를 주느니, 차라리 원본 스트림을 주는 게 맞다(Correctness)."
공식 문서 대신 "Javadoc"과 "Github"이 증거
스프링 팀은 이런 디테일한 변경을 문서보다는 **코드 주석(Javadoc)**이나 PR(Pull Request) 코멘트에 남깁니다.
실제로 ServletServerHttpRequest 클래스의 과거 히스토리나 관련 이슈를 뒤져보면, **"파라미터 기반의 바디 재구성은 신뢰할 수 없다(unreliable)"**라는 뉘앙스의 토론이 존재합니다.
특히 **Spring Framework 6 (Boot 3.0)**은 "Jakarta EE 스펙을 엄격하게 준수한다"는 대원칙 하에, 과거에 편의를 위해 제공하던 **"매직(Magic) 코드"**들을 대거 제거했습니다. 2.7의 그 코드는 사실상 **"개발자가 실수로 바디를 날려먹었을 때를 대비한 안전망(Hack)"**이었는데, 3.0에서는 **"정확성을 위해 그 안전망을 걷어낸 것"**입니다.
라고 설명하고 있다. 틀린 말은 아니다. 여하튼 이젠 strictly 하게 처리해야한다
그냥 거칠게 표현하면 이거다.
x-www-form... 의 콘텐츠타입인가?
그럼 RequestParam 으로 받아라. 파라미터니까! 기존에 되던건 사실 봐준거야...
이거다.
'IT > 스프링' 카테고리의 다른 글
| spring boot CORS .. cross domain (0) | 2018.07.16 |
|---|---|
| intelliJ 에서 스프링 mvc 시작하기 (0) | 2012.03.27 |
| 스프링 스테레오 타입... 어노테이션 , annotation !!!! (0) | 2012.03.21 |
| Autowired / Resource 차이... !!!!! (0) | 2012.03.15 |
| Bean .. 빈 객체 스캔하여 빈 등록 (0) | 2012.02.29 |