JSP(JSTL)과 Javascript의 혼파망 메뉴얼
Tags: korean, jsp, java, javascript
Published: 2019-09-11 08:01:16

이 글은 갓 시작한 자바 웹 개발자를 위한 글이다. 다른 언어로 웹 개발을 한다면 이 글은 저질이니 [절](https://namu.wiki/w/%EC%A0%88)로 꺼지기 바란다.

> javascript 변수를 `<c:set>` 값에 넣으려고 하는데 어떻게 하면 되나요?
> vo 배열을 javascript로 가져오는데 이상한 문자가 오고, `<c:forEach>` 반복문에 javascript 변수 참조가 안됩니다. 어떻게 해야 할까요?

아 시발... 좀... 제발 웹 개발자가 되려면 먼저 서버와 클라이언트의 기본부터 공부하기 바란다.
내가 질문하는 것 까진 뭐라 안하는데 이걸 질문하는 병신들이 왜이리 많은지 당장 OKKY만 가도 간간히 보인다.
잊을만 하면 또 올라오고. 아주 미치겠다. 내가 정리하겠다.
정리 하고도 싫으면 그냥 개발자 포기해라. 부탁이다. 다른 개발자를 위해서라도.

## `<c:set>` 태그로 자바스크립트 변수 설정하기

일단 기본적으로 이딴 질문 해봐야 돌아오는 답은 100% **불가능**하다 이다.
JSP(JSTL)은 서버단, JS 는 클라이언트단이라고 친절하게 설명까지 해줄 확률 99%다.
만약 `<c:set>` 태그로 자바스크립트 변수를 세팅하려는 목적은 내 경험상 아래에 해당하겠다.

- JSTL 처리의 편의성 (예를 들면 `<c:if>` 조건절 태그에 사용하기 위한)
- **자바스크립트 쓰기 싫어서**

이 질문을 하는 인간들을 보면 자바스크립트를 배우지 않거나, 배울 생각이 없거나 둘 중 하나다.
그래.. 뭐... 자바로만 웹 사이트를 구성했으면 얼마나 좋겠니. 편하긴 하지. 하나의 언어로만 고생하면 되니까.
혹시나 그렇게 생각하는 인간에게 내가 명치 한방 좀 때려주도록 하겠다.

> Java는 오라클의 등록상표입니다.

알겠냐? 자바가 아무리 오픈소스라도 자바를 함부로 타 앱에 갖다쓸 수 없다. 예를 들어 크롬이 자바를 자바스크립트처럼 쓰게 하려면 구글이 오라클에게 거액의 로열티를 지급하던가 해야 한다. 물론 구글은 안드로이드에 자바 쓰다가 오라클한테 뒷통수 후려맞아서 지금 코틀린으로 바꾸고 자바 어떻게든 걷어내려고 하고 있다.

자. 그렇다면 어떻게 해야 하나? 답은 하나다. 클라이언트 단인 자바스크립트를 쓰기 시작하면 서버단인 JSP(JSTL) 역할은 이미 완전히 끝난 거다. 스크립트 짜는 동안에는 JSP나 자바를 잊고 개발하기 바란다.

즉, 스크립트로 if 문 쓰고, 스크립트로 반복하라고. 케이스별로 공략해 주겠다.

### `if`

이런 JSP 코드가 있다 치자.

```jsp
<c:set var="isRight" value="false"/>

<script>
// condition에 따라 동적으로 서버단 조건 변경
if(condition == true) <c:set var="isRight" value="true"/>
</script>

<c:if test="${isRight}">
<p>내가 옳다.</p>
</c:if>
<c:if test="${not isRight}">
<p>내가 틀리다.</p>
</c:if>
```

어쩌면 초보인 여러분이 이렇게 짜고 싶어 근질근질 하겠지. 결론부터 말하자면...
너희들 뜻대로 안나올 것이다. JSP 렌더링 되는 소스 결과는 아래와 같다.

```html
<script>
// condition에 따라 동적으로 서버단 조건 변경
if(condition == true)
</script>


<p>내가 옳다.</p>
```
이런 식으로 렌더링 되면서 스크립트 오류는 덤으로 올 것이다.

> Uncaught SyntaxError: Unexpected end of input

그럼 어떻게 수정해야 할까? 

먼저 내가 한 말을 기억해라. script를 시작하면 JSP는 이미 끝났다.
대신, 값을 가져올 수는 있으니 값을 가져오라.

```jsp
<%-- 어쩌면 model로부터 가져올 수도 있을 테니 남겨 두겠다. --%>
<c:set var="isRight" value="false"/>
<script>
var text = document.getElementById('text');
// condition에 따라 동적으로 텍스트 변경
if(${isRight} && condition == true) {
  text.innerHTML = '내가 옳다.';
}
</script>

<p id="text">내가 틀리다.</p>
```

자. 이렇게 해서 서버단 변수 `isRight` 및 클라이언트단 변수 `condition` 을 true 혹은 false 로 원하는 대로 조절해서 브라우저에 띄워봐라. 그러면 원하는 결과가 나올 것이다.

### `for` `while` 등의 반복문

먼저 단순히 나열하는 건 된다. 예를 들어 `String[]` 이나 `List<String>` 이라면 뭐 불러오는 데 문제는 없을 것이다.

만약 POJO 객체라면 바로 불러와봐야 `toString()` 메소드로 불려진 문자열이 출력될 것이다. 그래서 만약 POJO 객체를 출력하고자 한다면, 평소처럼 POJO 객체의 `getter` 메소드를 호출하면 될 것이다.

스크립트에 추가해도 불러오는 건 매한가지.

여기까지가 읽기 전용이고, 이제부터 언급하는 건 바로 **변경**이다.
결론적으로 그냥 안 된다. 이제 설명 들어가도록 하겠다.

혹시 ajax를 아는가? ajax를 안다면, 당신은 굳이 이 글을 안봐도 될 정도의 레벨이다.
하지만 모른다면, 구글에 스프링 ajax를 쳐서 공부하기 바란다. 어자피 늬들이 해봐야 스프링 jsp 등등 밖에 더 되겠냐만은.

먼저, 이미 알고 있는 사실이겠지만, Java의 `List` 인터페이스 기반의 모든 클래스, 그리고 Java의 대괄호(`[]`)로 묶는 배열 역할의 추상 클래스 `Array`, Javascript의 배열을 담당하는 `Array` 함수는 태생이 애초부터 완전히 다르다. 호환할 수가 없다.

그리고, 자바는 타입을 엄격하게 따지지만, 자바스크립트는 그렇지 않다.
물론 이런 점 때문에 자바스크립트를 혐오하는 시니어 개발자도 존재하기는 하다.
그런 시니어 개발자에게는 타입을 엄격하게 따지면서 자바스크립트를 객체지향적으로 사용할 수 있는 자바로 착각할 만할 정도로 핫하고 핫한 [타입스크립트](/t/typescript)를 추천하자.

어쨌든, 보통 리스트를 불러올 때 `<c:forEach>`를 쓸 테고, 검색을 한다면 보통 폼 전송을 수행해서 다시금 `<c:forEach>` 태그로 반복문을 실행하여 렌더링을 수행할 것이다.

하지만 새로고침 없이 부분 목록을 불러와야 하거나, 동적으로 트리 구조 방식으로 선택하는 선택 란 등을 구현하고자 한다면, 결국 스크립트 떡칠을 고려할 수밖에 없다.
맞다. 떡칠해야 한다. 싫어도 어쩔 수 없다. 받아들이도록 하자.

만약 이렇게 동적으로 POJO 객체를 수정하고 싶다면,

```jsp
<script>

<c:forEach items="${voList}" var="vo">

  if("${vo.status}" == "R") {
    vo.statusText = "읽는 중"; // ???
  }

</c:forEach>

</script>

```

일찌감치 포기하는 게 좋다. 먼저 편집된 POJO 목록을 처리할 루틴을 호출할 방법이 없다.
게다가 이런 습관 절대 안 좋은 습관이다. 차라리 구문 별로 출력하는 게 좋다.
만약 vo 를 동적으로 바꾸고 다시 전달하고자 한다? 더더욱 안 된다.
아까도 말했지만 스크립트를 쓰는 순간 서버단 구문은 바이바이다.

이를 효율적으로 관리하고 싶다면, JSON을 이용하라.
Spring 을 쓴다면, Jackson 이 딸려올 것이다. 사용 법은 알아서 공부하고, JSON 문자열로 저장했다 치고, 이를 가공하는 방법을 예제로 설명하겠다.

준비물

- 컨트롤러의 메소드 2개 (View 렌더링과 POST API 하나)
- View jsp 하나
- JS 스킬

```jsp

<script>

  var voList = (${voListByJson});

  for(var i = 0; i < voList.length; i++) {
    var vo = voList[i];
    vo.someProperty = '아오지';
    //...
  }

  // jQuery 예를 들면,
  jQuery.post('/path/to/edit', voList, function(){
    alert('편집 완료!');
  });

</script>

```

API와 스크립트 기술을 어느정도 습득하면, 충분히 만들 수 있을 거라 믿는다.
만약 그러지 못하다면, [생활코딩](https://www.opentutorials.org/course/3281) 가서 공부하면 된다.

### JS 에서 JSTL(Java) 함수 호출

방금 들어온 따끈따끈한 질문을 보고 답변주면서 쓴다.
당연하겠지만 자바단에서 자바스크립트 함수를 호출할 수 없고, 반대로 스크립트단에서 자바 함수를 호출할 수도 없다. 당연히 스크립트단에서 JSTL 함수(`${fn:something()}`) 호출도 안된다.

```jsp
<script>

var a = 'Hey Apple!';
var b = '${fn:substring(a, 4)}';

window.load = function(){
    document.getElementById('some').innerHTML = b;
};

</script>

<p id='some'></p>
```

이렇게 해봐야 렌더링은 커녕 톰캣 콘솔에 예외 스택 추적이 너를 반겨줄 것이다.

해결책은 어찌보면 간단할 수도, 복잡할 수도 있다.
크게 2가지 케이스가 있는데, 내부적으로 해결 가능한거와 서버단일 빌려야 하는 경우가 있다.

먼저, 문자열을 변형하거나 꾸미는 등의 행위 (예: `substring` `replace` 등)의 경우나, 숫자에 3자리 쉼표 넣는 등의 기능은 자바에서도 가능하고 스크립트에서도 가능하다. 단지 서로 서버와 클라이언트라는 **철책**으로 나뉘어져 있으니, 각자 구현해야 한다. 번거롭겠지만 직접 구현하거나, 없으면 Stack Overflow 라던가 영어 못하면 네이버 검색이라도 하기 바란다.

만약, 서버단 기능이 불가피하게 필요한 경우 (예: DB 호출, 파일을 다루는 외부 리소스 활용)는 스크립트단에서 당연히 불가능하다. 이럴 땐 스크립트의 둘도 없는 친구 `ajax`를 활용하자.

준비물은 아래와 같으며, 준비하는 방법은 각자 숙제로 남기도록 하겠다.

- `ajax` 기술과 요청, 응답 콜백
- 서버단 `API`의 구현

어자피 다 공부해야 할 것들이야 임마.


이제 피곤하다.
이제 제발 좀 스크립트에 자바 호출하거나 값 설정하는 질문 좀 안봤으면 좋겠다.
그러길 바라겠다.

내가 이 글 하나 쓸려고 1주일동안 머리 짜매기도 지쳤다.

.. 끗