Category Archives: 허접프로그래머

[Java] Tag라이브러리(JSTL) 사용하기

자꾸 까먹는 경향이 있어 기록용으로 저장해 둡니다. JSP에서 간단한 프로그램 로직을 구현하기 위해 JSTL을 사용하는데요 다양한 JSTL용 태그 라이브러리가 제공되지만 이 글에서는 플로우등을 조정하기 위한 core만을 보도록 하겠습니다.

1. JSTL을 사용하기 위한 라이브러리를 다운

[이곳] 에서 다운받을 수 있습니다. 현재 시점에서는 1.1.2가 최신버전이군요. 만약에 Gradle 기반 프로젝트에서 JSTL을 사용하고자 하신다면 다음을 추가하면 됩니다.

dependencies {
	compile 'javax.servlet:jstl:1.2'
}

2. 라이브러리 추가

다운받은 파일을 열어보면 standard.jar jstl.jar 두개의 파일이 존재합니다. 둘 모두를 개발중인 프로젝트에 추가합니다.

3. JSP 페이지의 맨 위에 taglib 정의 추가

프로젝트의 맨 위에 다음을 추가해 줍니다.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

4. JSTL 문법을 사용 (몇가지 예시)

<c:forEach>를 사용한 특정 범위의 숫자값을 순환

<table>
<tr>
  <th>Value</th>
  <th>Square</th>
</tr>
<c:forEach var="x" begin="0" end="10" step="2">
<tr>
  <td><c:out value="${x}"/></td>
  <td><c:out value="${x * x}"/></td>
</tr>
</c:forEach>
</table>

위의 코드는 0부터 10까지 2씩 증가하는 순환문을 뜻합니다. 현재의 증가값은 변수 x에 저장됩니다. 결과는 다음과 같이 출력이 됩니다.
사용자 삽입 이미지
<c:forEach>태그를 이용한 Collection형 배열을 처리

forEach는 Collection, Map, Iterator, Enumeration, Array(Object/Primitive), 쉼표로 구분된 String, SQL쿼리 결과값(javax.servlet.jsp.jstl.sql.Result) 등의 순환을 지원합니다.

<table>
  <c:forEach items="${entryList}" var="blogEntry">
    <tr><td align="left" class="blogTitle">
      <c:out value="${blogEntry.title}" escapeXml="false"/>
    </td></tr>
    <tr><td align="left" class="blogText">
      <c:out value="${blogEntry.text}" escapeXml="false"/>
    </td></tr>
  </c:forEach>
</table>

위의 모드는 블로그 글을 순환하며 출력하는 예시입니다. ${entryList}는 title, text를 멤버 변수로 갖는 특정 객체의 집합 배열입니다. 현재 순환중인 객체가 blogEntry에 저장되며 .을 사용하여 title과 text를 출력하는 예시입니다.

여기에 새롭게 현재 순환중의 상태를 확인할 수 있는 varStatus라는 값을 사용할 수 있습니다. 말을 길게 할 필요없이 코드로 보여드리겠습니다.

<table>
  <c:forEach items=
    "${entryList}" var="blogEntry" varStatus="status">
    <tr><td align="left" class="blogTitle">
      <c:out value="${status.count}"/>.
      <c:out value="${blogEntry.title}" escapeXml="false"/>
    </td></tr>
    <tr><td align="left" class="blogText">
      <c:out value="${blogEntry.text}" escapeXml="false"/>
    </td></tr>
  </c:forEach>
</table>

위에 추가된 ${status.count}에서는 현재 몇번째 순환중인지 값을 확인할 수 있습니다.
사용가능한 변수의 종류는 다음과 같습니다.

  • current : 현재 순환중인 아이템을 가져옵니다.
  • index : 현재 순환중인 아이템의 인덱스(0베이스)를 가져옵니다.
  • count : 현재 순환중인 아이템의 인덱스(1베이스)를 가져옵니다.
  • first : 현재 순환중인 아이템이 첫번째 아이템인지 여부를 확인합니다. (Boolean)
  • last : 현재 순환중인 아이템이 마지막 아이템인지 여부를 확인합니다. (Boolean)
  • begin : forEach에서 지정할 수 있는 begin값을 가져옵니다.
  • end : forEach에서 지정할 수 있는 end값을 가져옵니다.
  • step : forEach에서 지정할 수 있는 step값을 가져옵니다.

<c:if>를 사용한 조건문 활용

다음의 코드는 첫번째 아이템이 순환중일 경우 블로그글이 언제 작성되었는지 날짜를 출력하도록 수정된 코드입니다. test안에 Boolean형이 반환될 수 있는 어떤 수식을 사용해도 됩니다.

<table>
  <c:forEach items=
    "${entryList}" var="blogEntry" varStatus="status">
    <c:if test="${status.first}">
      <tr><td align="left" class="blogDate">
            <c:out value="${blogEntry.created}"/>
      </td></tr>
    </c:if>
    <tr><td align="left" class="blogTitle">
      <c:out value="${blogEntry.title}" escapeXml="false"/>
    </td></tr>
    <tr><td align="left" class="blogText">
      <c:out value="${blogEntry.text}" escapeXml="false"/>
    </td></tr>
  </c:forEach>
</table>

그런데 정말 희안하게도 위의 <c:if>에는 else가 존재하지 않습니다. 그래서 다음의 방법을 사용하곤 합니다.

<c:choose>를 사용한 다중 조건문 활용

다음의 예제는 pageContext라는 컨텍스트 객체에 접근하여 요청의 스킴을 읽어오는 예제입니다. HTTP로 접속했을때와 HTTPS를 통해 접속했을때 다른 메시지를 출력하는 예시입니다. 추가로 둘다 아닐경우 오류 메시지를 출력하도록 하였습니다.

<c:choose>
  <c:when test="${pageContext.request.scheme eq 'http'}">
    This is an insecure Web session.
  </c:when>
  <c:when test="${pageContext.request.scheme eq 'https'}">
    This is a secure Web session.
  </c:when>
  <c:otherwise>
    You are using an unrecognized Web protocol. How did this happen?!
  </c:otherwise>
</c:choose>

<c:url>를 사용하여 주소 생성

JSTL에서는 <c:url>을 지원하는데요 이 태그는 현재의 서블릿 컨텍스트 이름을 자동으로 앞에 붙여주고 세션관리와 파라미터의 이름과 값의 인코딩을 자동으로 지원합니다. 기본적인 사용법은 다음과 같습니다.

<a href="<c:url value='/content/sitemap.jsp'/>">View sitemap</a>

간단하죠? 여기서 더 나아가 <c:param>을 사용하여 파라미터를 추가할 수 있습니다.

<c:url value="/content/search.jsp">
  <c:param name="keyword" value="${searchTerm}"/>
  <c:param name="month" value="02/2003"/>
</c:url>

위의 코드로써 생성되는 URL은 기본적으로 서블릿컨텍스트가 붙게 되며 세션쿠키를 사용중이라면 추가적으로 파라미터들만 추가되며 다음의 모습을 가지게 됩니다.

/blog/content/search.jsp?keyword=foo+bar&month=02%2F2003

만약에 세션 쿠키가 존재하지 않는다면 다음과 같은 결과를 나타내게 됩니다. 마찬가지로 파라미터들은 URL 인코딩되어 출력됩니다.

/blog/content/search.jsp;jsessionid=233379C7CD2D0ED2E9F3963906DB4290
?keyword=foo+bar&month=02%2F2003

<c:import>를 사용하여 페이지 첨부하기

JSP에는 기본적으로 두가지 방법의 페이지 안에 다른 컨텐츠를 추가하는 방법이 존재합니다. include지시자와 <jsp:include> 액션이 있는데요. 하지만 둘 모두 같은 웹 어플리케이션 또는 서블릿 컨텍스트 안에있는 페이지만을 불어들일 수 있습니다. core라이브러리에 있는 <c:import> 액션은 좀더 일반적이고 강력한 기능을 가진 <jsp:include>로 볼 수 있습니다. 사용 문법은 <c:url>과 매우 배슷하며 심지어 <c:param>도 그대로 사용할 수 있습니다.

<c:import url="ftp://ftp.example.com/package/README"/>

<c:import>에는 var와 scope 두가지 필수적이지 않은 속성이 존재하는데요. var의 경우에는 불러들인 페이지를 곧바로 출력하지 않고 String형 변수로 담아두기 위해 사용됩니다. scope는 이 변수의 스코프를 지정할 수 있습니다. 기본적으로 page로 되어있습니다.

<c:catch>로 예외처리 하기

길게 설명할 필요가 없을것 같네요. <c:import>는 ftp에도 접속이 가능합니다. 다음에 보여드릴 코드의 경우 만약에 해당 위치에 파일이 존재하지 않거나 네트워크의 문제로 페이지를 불러올 수 없는 상황이라면 예외가 발생할 것입니다. 예외가 발생할 경우 var에 예외가 저장됩니다. <c:if>를 통해 예외가 발생했는지 확인하는 예제입니다

<c:catch var="exception">
  <c:import url="ftp://ftp.example.com/package/README"/>
</c:catch>
<c:if test="${not empty exception}">
  Sorry, the remote content is not currently available.
</c:if>

<c:redirect>를 이용한 페이지 리다이렉트 하기

이 액션은 <jsp:forward> 액션과도 매우 흡사합니다. 하지만 이 기능의 경우에는 서버사이드에서 구현된 요청형태만을 포워딩 합니다. 이게 무슨 말이냐면 포워딩의 경우에는 사용자 입장에서 보면 페이지의 이동 없이 다른 페이지를 띄워줄 수 있지만 리다이렉트의 경우에는 브라우저에 의해 페이지의 이동이 일어나게 됩니다. 하지만 <c:redirect>액션이 좀 더 유연합니다. <jsp:forward>의 경우에는 현재 같은 서블릿 컨텍스트 내의 다른 페이지로만 이동 할 수 있기 때문입니다.

참고 : http://www.ibm.com/developerworks/java/library/j-jstl0318/

[Java] 현재 시스템의 IPv4기반의 IP주소 가져오기

Java에서 IP주소를 가져오는 방법은 매우 쉽고 간단합니다. 일반적으로 사용되는 간단한 방법으로는 다음과 같은 방법이 있습니다.
[code]InetAddress.getLocalHost().getAddress()[/code]
위의 단 한줄로 현재 시스템의 IP를 읽어올 수 있습니다. 그런데 개발시에는 문제가 없었는데 리눅스 서버에서 구동해 보니 127.0.0.1과 같이 loopback 주소가 나오는 경우가 있더군요. 찾아보다 다음과 같은 방법으로 깔끔하게 해결할 수 있었습니다.
[code]/**
 * 현재 서버의 IP 주소를 가져옵니다.
 *
 * @return IP 주소
 */
private String getLocalServerIp()
{
try
{
   for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();)
   {
       NetworkInterface intf = en.nextElement();
       for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
       {
           InetAddress inetAddress = enumIpAddr.nextElement();
           if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress())
           {
            return inetAddress.getHostAddress().toString();
           }
       }
   }
}
catch (SocketException ex) {}
return null;
}[/code]
현재 시스템의 모든 네트워크 인터페이스를 읽어와서 loopback장치인지 랜선에 연결된 장치인지 여부를 확인하여 실제 사용중인 인터페이스의 IP주소를 읽어오게 됩니다. 위와 같이 사용해서 실무에 적용해본 결과 어느 OS, 장치에 상관없이 IP가 정상적으로 출력되는것을 확인하였습니다.

소스코드가 좀 길고 더럽네요; 그냥 보관용으로 적어두는데 의미를 부여하겠습니다;;