JavaScript

[JavaScript] 백엔드가 자바스크립트 때문에 골치아팠던 건에 대하여

정희재 2022. 3. 30. 00:39

자바스크립트는 프론트의 영역인데 왜 백엔드가 이걸 가지고 골 아픈짓을 하고 있는가.

사람은 예술적 감각을 어느정도 갖고 있기 때문에 아무리 백엔드라도 밍밍한 웹을 보면 도저히 신경질 나서 견딜 수가 없다.

그래서 어느 정도 최소한의 인간적인 UI는 구축하고자 스크립트를 짠다.

물론 분업이 프론트엔드와 백엔드 분업이 확실한 상황에서는 그럴 일이 거의 없겠지만 혼자서 공부를 한다거나 토이 프로젝트를 하는 경우에는 반드시 UI를 건드려야만 성이 찬다.

내가 지금 그 상황이다.

그런데 내 주전공이 아닌 자바스크립트 때문에 아주 골아팠던 적이 있다.

알다가도 모를 친구, 자바스크립트...

 

"백엔드가 자바스크립트 때문에 골치아팠던 건에 대하여" 잠시 짚고 넘어갈 필요가 있다.

 

attr() 과 prop() 의 차이

서버로부터 받아온 데이터에 따라서 자동으로 체크박스를 체크하는 코드를 짜고 있었다.

여기에 jQuery를 사용했다.

jQuery ... 요즘 저물어가는 기술이기는 하지만 그래도 꽤나 짱짱하고 좋은 기술임은 변함이 없다.

하여간 jQuery 의 attr() 을 이용해서 checked 속성을 부여 해주려고 했다.

근데 체크가 안된다.

왜?

attr() 대신 prop() 을 써야했다.

두 메소드에는 차이점이 있다.

 

attr() : 속성 그 자체의 값을 반환한다. 주로 element가 가지는 속성 값이나 정보(style, src, rowspan 등)를 조회하거나 세팅하는데 사용한다.

 

prop() : 속성 값을 명시적으로 반환한다. 주로 element가 가지는 실제적인 상태(활성화, 체크, 선택 여부 등)를 제어할 때 주로 사용한다.

 

뭔 소리인지 정확히는 모르고 살짝만 감이 와서 실험을 좀 해봤다.

 

체크박스 실험실

 

체크 박스를 하나 만들고 버튼 2개를 만든다.

그리고 각각 onClick 이벤트를 달아주는데, 하나는 attr() 을 이용해 체크하는 방법.

하나는 prop() 을 이용해 체크하는 방법이다.

테스트 해보자

 

체크 실험

처음에 attr로 체크해보기를 하면 체크가 잘된다.

prop으로 체크해보기 역시 체크가 잘된다.

그런데 prop으로 체크해보기를 눌렀다가 attr로 체크해보기를 누르면 체크가 안된다.

 

attr로 checked 옵션을 추가해주는 것은 말 그대로 체크박스의 초기값을 체크상태로 두겠다는 것이다.

체크박스에 어떤 값도 들어가있지 않은 상태에서 초기값을 바꿔주는 것이기 때문에 체크가 잘 된다.

그런데 prop으로 체크를 해주면 이제 체크박스는 true 혹은 false 값을 가지게 된다.

그 상태에서 attr로 다시 체크 여부를 바꿔주려고 해도 바뀌지 않는다.

왜냐하면 체크박스의 값은 이미 prop으로 정해놨기 때문이다.

 

기능좀 하나로 통일 시켜줬으면 좋겠다. 

 

ajax의 processData, contentType 속성에 대해

게시판에 파일 첨부 기능을 만들고 있었다.

파일을 유동적으로 추가하고 삭제할 수 있도록 FormData를 이용했고 파일첨부는 ajax를 이용해 전송하기로 했다.

그런데 대체 무슨 이유인지 서버 측에서 데이터를 제대로 못받는다.

 

그 이유를 바로 알아보자.

아래 코드는 파일을 전송할 때 필요한 ajax의 구성요소다.

 

$.ajax({
    url : "/Post/FileUploadProcess"  // 요청할 URL
    data : formData                  // 전송할 데이터(FormData)
    dataType : "json",               // 응답으로 받을 데이터 타입
    processData : false,             
    contentType : false,
    type : "POST",                   // HTTP Method
    success : fnSuccess,             // 성공 시 실행할 함수
    error : fnError                  // 실패 시 실행할 함수
});

 

자 여기서 processDatacontentType 만 주석이 빠졌는데 왜냐

내가 설명할거니까.

일단 나는 저거 2개를 빼먹었기 때문에 데이터 전송이 안됐다.

별로 필요해보이지도 않는 속성이라 일부러 안넣었다.

그런데 파일전송에 있어서 은근히 중요한 역할을 하는 속성이었다.

 

ajax는 기본적으로 전송할 데이터를 query string으로 만들어서 전송한다.

근데 파일 데이터를 query string으로 만들어서 보낸다?

아주 터무니 없는 생각이다.

processData를 false로 지정해주면 query string이 아닌 다른 방식으로 보낸다.

(어떤 방식인지 자세하게 알아보지는 않았는데 query string이 아니면 request body 밖에 없지 않을까...)

 

contentType은 기본값이 application/x-www-form-urlencoded;charset=utf8 이다.

기본값 주제에 뭐같이 길다.

하여간 저걸 바꿔줘야한다.

파일 전송할때의 contentType은 multipart/form-data; boundary="바운더리값"

이건데, 일단 너무 길어서 직접 타이핑치기 싫고 바운더리값이라는게 뭔지도 몰라서 써먹기 싫다.

이때 contentType을 false로 지정해주면 알아서 저렇게 바꿔준다고 한다. (진짜 개연성없다)

 

바운더리의값이 무엇이냐

FormData로 데이터를 전송하면 바이너리 데이터로 전송이 되는데 모든 데이터가 붙어서 전송된다.

만약 파일 3개를 보낸다고 하면 어디까지가 하나의 파일인지 구분이 안간다는 뜻이다.

그래서 이걸 구분해주기 위해 데이터 사이사이에 바운더리값을 넣어준다는데... 뭐 그렇다고 한다.

그래서 저 "바운더리값"은 그냥 아무 문자열이나 넣으면 된다.

 

FormData 초기화 시키기

이건 게시판 임시저장 기능을 만들다가 만난 이슈다.

글 하나를 열고 파일 첨부를 막 하다가 임시저장을 누른 후 다른 임시저장 글을 불러오는 테스트를 해봤다.

그리고 글 작성 버튼을 눌렀더니 파일 첨부가 안된다.

이전에 임시저장할 때 FormData에 추가됐던 위조방지토큰이 그대로 들어있었기 때문이다.

뭐 글로 설명하면 어떤 상황인지 잘 모를테니까

한마디로 설명하자면

임시저장을 누른 후에는 반드시 FormData 를 초기화 시켜줘야만 했다.

그래서 FormData 초기화 함수를 만들었다.

한번에 초기화를 하는 FormData.clear() 같은 메소드가 있었다면 참 좋겠지만 아쉽게도 그런건 없었다.

직접 순회하면서 하나씩 제거를 해줘야 했다.

스택오버플로우를 뒤지다가 얻어낸 코드는 이거다.

추천 수가 꽤나 높은 코드였다.

formData.forEach(function(value, key, fd){
    console.log(key);
    formData.delete(key);
});

 

 

아주 직관적이고 쉬운 코드다.

그런데 대체 어떤 사이비들이 이 코드에 추천을 그렇게나 눌렀는지 의심된다.

이 코드만 믿고 한참을 버그와 싸웠다.

 

저 코드를 가지고 테스트를 해보겠다.

FormData 를 하나 만들고 대충 위조방지토큰, 게시글 번호, 이미지파일 3개를 넣어봤다.

그리고 FormData를 순회하면서 콘솔 로그를 찍고 하나씩 지워봤다.

 

FormData 테스트

 

콘솔 로그에 5개가 아니라 3개만 찍힌다.

왜인지 눈치빠른 사람들은 바로 알아차리겠지만 난 이걸 왜 한참 뒤에 알았을까

formData를 순회하는데 그중 하나를 지우면 formData의 원소들이 바뀐다.

맨 처음에 __requestToken 이라는 값을 지운 뒤에는 key가 postIndex 를 가리키고 있어야 하지만

__requestToken이 사라진 뒤 formData의 두번째 원소는 abc.jpg 가 된다.

그래서 postIndex는 건너 뛰게 된다.

결국 모든 데이터가 다 지워지지는 않는 것이다.

이런 젠장.

 

코드는 이렇게 고쳐봤다.

 

FormData 테스트

formData의 키배열을 만들고 그 배열을 순회하면서 formData의 원소들을 지웠다.

그런데 이게 무슨일이야...

결과는 똑같다.

이때 황교수님의 자바스크립트 메모리 관리 강의를 떠올렸다.

formDataKeys는 formData의 키 목록을 가지고 있고...

formData의 원소가 하나 지워지면 formData의 키 목록이 하나 줄어든다...

formDataKeys는 formData의 키 목록을 Deep Copy해서 가지고 있는게 아니라 formData의 키 목록을 가리키고 있는 것이다...

그렇다는건... formDataKeys의 원소도 하나 줄어든다...

아 !!

 

코드를 다시 바꿨다.

 

FormData 테스트

빈 배열을 하나 만들고 그 안에 FormData의 키를 넣어준 뒤

그 배열을 순회하면서 제거하면 이건 진짜 안될리가 없다.

 

하여간... 자바스크립트랑 친해지려면 시간이 많이 필요할 것 같다.

빨리 실무에 투입돼서 백엔드만 열심히 했으면 좋겠다.

(사실 백엔드 일을 한다고해도 스크립트 코드를 짜야하는 경우는 가끔 생길것 같다...)