언제나 새버전의 OS를 접하는 것은 난감함의 연속인것 같습니다.

이것 저적 작업 하다 보면 보면 참고할 만한 자료가 없어 애먹는 경험이 많아서 최신버전 보다 한단계 낮은 버전을 선택 하곤 하는데도 했는데도 몇번의 시행착오를 격었습니다.

특히 MySQL 5.7 버전은 이전 버전과 몇가지 바뀌어서 약간 많은 애를 먹었습니다.


얼마전 지인으로 부터 윈도우+MSSQL에서 돌아가고 있는 스트럿츠 기반의 어플리케이션을 Linux  + MySQL  기반으로 옮겨 달라는 의뢰를 받았습니다.

서버를 납품하는 업체의 엔지니어에게 OS를 설치 해달라고 요청 한 후 몇일이 지나서 IP가 할당되었다고 계정과 암호를 받아 접속 해 보니 CentOS 7 64bit 버전을 풀 인스톨 해 놓은듯... 

에초에 모든게 세팅된 상황에서 소스와 DB 만 옮겨 주겠다고 신신당부하고 시작했는데... 결국엔 재설치를 위해 CentOS 7 64bit Minimal  iso를 USB에 부팅 이미지로 만들어 서버실에 방문하고야 말았습니다. ㅠㅠ v


CentOS 7 minimal  설치는 간단한 구글링 으로도 많이 나오니 참고 하시기 바랍니다.

(https://joungkyun.gitbooks.io/annyung3-installation-guide/content/chapter1.html)


네트워크 설정은 가능한 install 하면서 GUI화면에서 설정하는게 편합니다.

부팅되면 네트워크가 활성화 되어 있는지 먼저 확인 했습니다.

[root@localhost ~]# ping 8.8.8.8

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.

64 bytes from 8.8.8.8: icmp_seq=1 ttl=41 time=56.3 ms

64 bytes from 8.8.8.8: icmp_seq=2 ttl=41 time=56.4 ms

64 bytes from 8.8.8.8: icmp_seq=3 ttl=41 time=55.0 ms

64 bytes from 8.8.8.8: icmp_seq=4 ttl=41 time=55.4 ms

^C

--- 8.8.8.8 ping statistics ---

4 packets transmitted, 4 received, 0% packet loss, time 3004ms

rtt min/avg/max/mdev = 55.082/55.832/56.425/0.633 ms

[root@localhost ~]#


네트웍이 정상적이지 않은경우 /etc/sysconfig/network-scripts/ifcfg-eth0 을 확인해 보시기 바랍니다. (ifcfg-eth0 명은 사용자의 네트워크 인터페이스 구성에 따라 다를 수 있습니다.)

많은 변수들이 있지만 중요한 것은 아래 네가지 정도 확인하시면 됩니다.

BOOTPROTO="static"

ONBOOT="yes"

IPADDR="192.168.0.240"

NETMASK="255.255.255.0"

GATEWAY="192.168.0.1"

DNS1="8.8.8.8"

제가 작업한 서버는 망을 분리해서 사설 IP를 사용하고 있습니다.


이제 몇가지 기본적인 utility 를 설치합니다.

[root@localhost ~]# yum -y install bind-utils wget elinks vim net-tools
[root@localhost ~]# yum -y install yum-utils sysstat system-config-firewall-tui
[root@localhost ~]# yum -y install man man-pages kernel-doc

이제 ifconfig로 네트워크 상태를 확인할 수 있습니다.


다음은 방화벽 설정

CentOS 7 부터는 iptables를 좀더 간단하게 사용할 수 있도록 새로운 firewalld 를 제공한다고 합니다.

자세한 내용은 구글링 해보면 많은 문서가 나오니 참고 하시고

/etc/firewalld/zones/public.xml 파일에 설정를 살펴보면 기본적으로   dhcpv6-client와 ssh만 허용되어 있습니다.

우선은 모든 외부 접속을 차단하고 내부망, 특정 사무실과 제가 작업할 장소에서만 SSD 접속이 가능하도록 설정하겠습니다.

/etc/firewalld/zones/public.xml 내용입니다.

<?xml version="1.0" encoding="utf-8"?>

<zone>

  <short>Public</short>

  <description>For use in public areas. You do not trust the other computers on ...</description>

  <service name="dhcpv6-client"/>

  <rule family="ipv4">

    <source address="192.168.0.0/24"/>

    <service name="ssh"/>

    <accept/>

  </rule>

  <rule family="ipv4">

    <source address="xxx.xxx.xxx.xxx"/>

    <service name="ssh"/>

    <accept/>

  </rule>

  <rule family="ipv4">

    <source address="yyy.yyy.yyy.yyy"/>

    <service name="ssh"/>

    <accept/>

  </rule>

</zone>

192.168.0.0/24 이렇게 대역(192.168.0.*)으로 룰을 추가 할 수 도 있고 xxx.xxx.xxx.xxx 단일 IP만 기술 하면 허용할 IP를 특정 할 수도 있습니다.

service name 으로 허용 할 수도 있고 port로도 허용할 수있습니다. 향후 필요에 따라 서비스 또는 포트를 추가 할 것입니다.

자세한 것은 [CentOS 7 minimal 설치 #4] 방화벽(firewalld) 설정 하기편을 참고하거나

 구글링 GO~ (https://www.lesstif.com/ws/네트웍-구성과-방화벽/firewalld)


설정을 변경 했으면 방화벽을 재시작 하여 설정을 반영합니다.

[root@localhost ~]# systemctl restart firewalld


이제 외부 접속 할 수 있는 ssh데몬을 설치 합니다.

[root@localhost ~]# yum -y install openssh-server openssh-clients

(SSH 데몬은 위에서  설치한 net-tools 패키지에 포함되어 있습니다.)



사용자 추가

[root@localhost ~]# groupadd -g 1000 dev <-- 사용자그룹 추가

[root@localhost ~]# cat /etc/group

root:x:0:

bin:x:1:

daemon:x:2:

... 생략 ...

dev:x:1000: <-- 새로 추가한 사용자 그룹

[root@localhost ~]# useradd -g 1000 -u 1000 webservice <-- 사용자 추가

[root@localhost ~]# passwd webservice <-- 생성한 사용자 비밀번호 설정

webservice 사용자의 비밀 번호 변경 중

새  암호:

새  암호 재입력:

passwd: 모든 인증 토큰이 성공적으로 업데이트 되었습니다.

[root@localhost ~]#


서비스 환경이 여러대의 머신이라면 가능하면 그룹아이디(gid)와 유저아이디(uid)를 동일하게 맞춰주는 것이 좋습니다.

시스템간에 파일을 이동하거나, 네트웍 스토리지등을 사용할 때 gid와 uid가 동일하면 추가로 오너쉽이나 퍼미션을 지정하지 않아도 됩니다.


root 권한 ssh 로그인 금지 설정

사용자들이 ssh를 이용해 root 권한으로 접근을 허용하는 경우 외부에서 리눅스의 취약점을 이용해 root 권한을 탈취하려고 시도하는 경우가 매우 많습니다.

조금 불편하더라도 root 권한은 콘솔에서만 접근하거나, ssh로 접근하는경우 다른 계정으로 로그인해서 Switch User (su -) 명령으로 권한을 획득해서 사용하는 것이 좋겠습니다.

[root@localhost ~]# vi /etc/ssh/sshd_config

... 생략 ...

#PermitRootLogin yes <-- 기존에 주석으로 되어 있던 것을 해제하고 yes => no 로 바꾼다.

PermitRootLogin no

... 생략 ...


자 이제 외부에서 접속 할 수 있는 준비는 완료 하였습니다.

서블릿 개발하기 연재목록

#1 개발환경 구축 (https://opensrc.tistory.com/180?category=475522)

#2 프로잭트 생성 (https://opensrc.tistory.com/181?category=475522)

#3 첫번째 JSP 파일 만들기 (https://opensrc.tistory.com/182?category=475522)

#4 첫번째 서블릿 만들기 (https://opensrc.tistory.com/183?category=475522)

#5 포스트 요청을 처리하는 서블릿 만들기 (https://opensrc.tistory.com/203?category=475522)

#6 GET/POST 요청과 함께 파라메터 전달 하기 (https://opensrc.tistory.com/204?category=475522)

#7 Servlet 들여다 보기 (https://opensrc.tistory.com/206?category=475522)

===========================================================




이제 첫번째 서블릿을 만들어 보겠습니다.

좀 식상하긴 하지만 화면에 hello, world 라고 출력하는 서블릿을 만들어 보도록 하겠습니다.

많은 프로그래밍 서적에 처음 작성하는 프로그램의 단골로 등장하는 프로그램입니다.


hello, world 는 UNIX OS를 만든 Dennis M. Ritchie가 UNIX를 만들기 위해 만든 C언어를 대중에 소개하기 위해 1978년에 쓴 The C PROGRAMMING LANGUAGE 라는 책의 1장에 나오는 프로그램입니다. 

#include <studio.h>
main()
{
    printf("hello, world\n");
}

마치 이후로 40년이 넘는 세월 동안 수 많은 시스템을 만드는 언어가 될 것을 예고라도 하듯이 '세상아 안녕' 하는 느낌입니다. 


이것을 순수하게 현대의 자바 코드로 바꾼다면

class HelloWorld
{
    public static void main(String[] args)
    {
        System.out.print("hello, world\n");
    }
}

이정도 코드로 만들면 될 것 같습니다. 우리는 이것을 HelloWorldServlet.java 라는 이름의 서블릿 프로그램으로 작성해 보도록 하겠습니다.

Java 코드를 먼저 보여드린 이유는 서블릿은 많은 플렛폼과 통신을 해야 하는 프로그램이기 때문에 Java 코드 몇 줄로 뚝딱 나오지 않는 다는 것을 비교하기 위해서 입니다.


각설 하고 이제 HelloWorldServlet.java 를 생성해 보도록 하겠습니다.

먼저 서블릿을 담을 패키지를 먼저 생성하겠습니다. 저는 이 블로그 도메인을 기준으로 com.tistory.opensrc.basicservlet.servlet 으로 하겠습니다.

이클립스 Project Explorer에서 지난시간에 생성한 프로잭트에 오른쪽 마우스 클릭하여 New > Package 메뉴를 를 선택합니다.

패키지명에 원하시는 패키지명을 입력하세요.


패키지가 생성되었으면 서블릿을 만들 겠습니다.

생성된 패키지 오른쪽 마우스 클릭 New > class 메뉴 선택

아래 화면처럼 Name : HelloWorldServlet 라고 입력 합니다.

부모 클래스를 지정해 주도록 하겠습니다.

Superclass : 오른쪽으 [Browse...] 버튼을 클릭합니다.


Choose a type : HttpServlet 입력하면

Matching items 에 아래처럼 목록이 나옵니다.

HttpServlet - javax.servlet.http 선택 후 [OK] 클릭


[Finish]를 클릭 하면 아래와 같은 모양의 첫번째 서블릿이 생성됩니다.



자동으로 생성된 코드인데도 이상하게 HelloWorldServlet 에 warning 표시인 노란색 및줄과 라인번호 앞에 느낌표가 켜져있습니다.

내용을 확인해 보면... 시리얼 버전 ID를 입력하라는 경고입니다.

이것은 java.io.Serializable 인터페이스를 구현(implements)하는 클래스에 나타나는 특성입니다.

API를 확인해 보면 HttpServlet 이 java.io.Serializable 를 implemented 되었음을 알 수 있습니다.

(https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html)


위 메뉴에서 Add generated serial version ID 를 클릭해서 ID를 생성해도 되고 그냥 실행하면 JVM이 자동 생성해서 붙혀준답니다.

자세한 내용은 http://hyeonstorage.tistory.com/253 이것에 잘 설명이 되어 있으니 참고 하시길 바랍니다.



HelloWorldServlet.java

package com.tistory.opensrc.basicservlet.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 첫번째 hello, world 서블릿
 * @author cloud
 */
public class HelloWorldServlet extends HttpServlet {
	
	/**
	 * serial version id
	 */
	private static final long serialVersionUID = 701289870660846795L;

	/**
	 * GET 요청을 처리
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		response.setContentType("text/html"); // text html 형태로 출력 하겠다고 지정함
		PrintWriter out = response.getWriter(); // response로 부터 출력 장치를 확보
		
		// html 내용을 출력
		out.println("<html>");
		out.println("hello, world");
		out.println("</html>");
	}
}

URL을 호출 하면 응답 하도록 GET 메소드에 응답하는 doGet 메서드를 생성 했습니다.

doGet 메서드의 원형은 사용자로 부터 요청을 담고 있는 HttpServletRequest 와 응답을 할 HttpServletResponse 객체를 인자로 가지고 ServletException 과 IOException 을 발생 할 수 있습니다.

이외 소스 내용은 주석을 참고 하시기 바랍니다.


이제 우리가 HelloWorldServlet 이 사용자 요청을 받아서 실행 될 수 있도록 설정을 해야 합니다.

간단하게 어노테이션을 이용할 수 도 있지만. 원래의 목적대로 서블릿의 근본적인 부분을 알아가기 위해  web.xml 에 직접 세팅을 해보도록 하겠습니다.


WebContent\WEB-INF\web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>BasicServlet</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>HelloWorld</servlet-name><!-- 시스템 에서 사용할 서블릿의 이름 --> <servlet-class>com.tistory.opensrc.basicservlet.servlet.HelloWorldServlet</servlet-class><!-- 서블릿의 패키지명 포함 클래스명 --> </servlet> <servlet-mapping> <servlet-name>HelloWorld</servlet-name><!-- 위에서 선언한 서블릿 이름 --> <url-pattern>/hello</url-pattern><!-- 대응할 URL --> </servlet-mapping> </web-app>

<servlet>~</servlet>, <servlet-mapping>~</servlet-mapping> 내용을 입력합니다.

간단히 설명하면  윗쪽 <servlet>의 두줄은 com.tistory.opensrc.basicservlet.servet.HelloWorldServlet 을 HelloWorld로 서블릿의 이름을 지정하는 것이고

아래 <servlet-mapping> 사용자로 부터 /hello URI로 호출이 들어오면 위에서 지정한 HelloWorld 서블릿을 호출 하도록 지정 하는 것입니다.

결국 사용자가 주소창에 http://serveraddress/hello 라고 입력하면 위에서 작성한 HelloWorldServlet.java 가 실행되어서 화면에 hello, world 라고 출력 하도록 설정을 하는 것입니다.


모두 이상 없이 입력 되었으면

지난 시간에 설정한 Apache Tomcat v8.0 서버를 실행한 후 브라우져를 띄워서 주소창에 http://localhost/hello 라고 입력합니다.

지금까지 스텝을 잘 읽고 따라 했다면 아래와 같은 결과 화면을 만나게 될 것입니다.


화면 바탕에 마우스 오른쪽 버튼을 누르고 페이지 소스보기를 해보면


이것은 우리가 HelloWorldServlet.java 의 doGet 메서드 안에 기술 했던

		// html 내용을 출력
		out.println("<html>");
		out.println("hello, world");
		out.println("</html>");

이부분이 브라우저에 출력된 것임을 알 수 있다.


다음 시간에는 POST 메서드에 대응하는 doPost 메서드에 대해서 알아보도록 하겠습니다.

서블릿 개발하기 연재목록

#1 개발환경 구축 (https://opensrc.tistory.com/180?category=475522)

#2 프로잭트 생성 (https://opensrc.tistory.com/181?category=475522)

#3 첫번째 JSP 파일 만들기 (https://opensrc.tistory.com/182?category=475522)

#4 첫번째 서블릿 만들기 (https://opensrc.tistory.com/183?category=475522)

#5 포스트 요청을 처리하는 서블릿 만들기 (https://opensrc.tistory.com/203?category=475522)

#6 GET/POST 요청과 함께 파라메터 전달 하기 (https://opensrc.tistory.com/204?category=475522)

#7 Servlet 들여다 보기 (https://opensrc.tistory.com/206?category=475522)

===========================================================




이제 간단한 JSP 파일을 하나 만들어 보겠습니다.

Project Explorer 에서 WebContent 디렉토리를 마우스 오른쪽 버튼으로 클릭

New > Other 클릭

Web > JSP File 선택 후 [Next >]

File Name 에 index.jsp 입력 후 [Finish] 클릭

body 와 body사이에 hello, world 라고 입력 후 저장 (Alt + S)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
hello, world
</body>
</html>


브라우저에서 첫번째 웹페이지를 확인합니다.

여기까지 잘 오셨다면 절반은 성공하신 겁니다.

시작이 반이라는 옛말에 의하면 말입니다.


심화 : JSP 라이프 사이클

지금 부터 설명하는 부분은 잘 이해가 안가시는 분은 한가지만 기억하고 건너뛰셔도 됩니다.

'JSP 파일은 실제 서버에서 실행되기 전에 Servlet 변환된 후 실행 된다.'

요것 만 이해하면 아래 내용은 건너 뛰셔도 관계 없습니다.


자 이제부터 JSP가 서블릿에서 실행되기 위해 어떤 과정을 거치는지 살펴 보겠습니다.

서버의 설정에 따라 조금 다르지만 일반적으로 JSP 파일을 새로 만들거나 파일의 내용을 수정한 후, 사용자로 부터 해당 JSP로 요청이 들어오면 서블릿 컨테이너가 JSP파일이 변경되는지 감시하고 있다가 새로 생성된 JSP 파일인 경우 해당하는 Servlet의 클래스가 존재 하지 않거나, class 파일의 생성 시간이 JSP파일의 최종 변경된 시간 보다 오래 되었다면 서블릿 컨테이너(WAS)는 JSP를 Servlet 코드로 변환한뒤 .class로 컴파일 하고 메모리로 로딩한 후에 비로서 실행합니다.

진짜 그런지 우리가 만든 JSP 파일을 살펴보도록 하겠습니다.


Server 텝에서 현재 가동하고 있는 서버를 더블클릭

Open launch configuration 클릭

Arguments 텝 클릭

VM arguments 부분에

-Dcatalina.base="..." 에 해당하는 문자열 복사

탐색기 주소창에 붙혀넣기 후 엔터

이후 다음 경로대로 하위 디렉토리를 탐색한다.

work\Catalina\localhost\ROOT\org\apache\jsp

아래 그림처럼 우리가 만든 JSP가 서블릿으로 변환된 java 파일과 그 서블릿이 컴파일된 class 파일 두개가 생성되어 있습니다.


index_jsp.java 파일의 내용을 구경하겠습니다.

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/8.0.36
 * Generated at: 2017-12-25 14:51:46 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map _jspx_dependants;

  private static final java.util.Set _jspx_imports_packages;

  private static final java.util.Set _jspx_imports_classes;

  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = null;
  }

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map getDependants() {
    return _jspx_dependants;
  }

  public java.util.Set getPackageImports() {
    return _jspx_imports_packages;
  }

  public java.util.Set getClassImports() {
    return _jspx_imports_classes;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
        throws java.io.IOException, javax.servlet.ServletException {

final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("Insert title here\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("hello, world\r\n");
      out.write("\r\n");
      out.write("\r\n");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

간단한 JSP 코드가 많은 내용이 더해진 채로 변환되어 있습니다.


중요한 부분 몇가지만 확인하면... 

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase

해당 클래스는 org.apache.jasper.runtime.HttpJspBase를 상속 받았다는 것을 알 수 습니다.

HttpJspBase 의 API 문서는 

https://tomcat.apache.org/tomcat-4.0-doc/jasper/docs/api/org/apache/jasper/runtime/HttpJspBase.html

이곳에서 볼 수 있습니다.

HttpJspBase는 javax.servlet.http.HttpServlet 을 상속 받은 서블릿이라는 것을 추측 할 수 있습니다.

소스의 자세한 부분은 본 문서의 범위를 벗어날 수 있으므로 흥미가 있는 분들만 확인하도록 하고

73라인의 _jspService 메서드에 대해서만 간단히 보겠습니다.

(실제 소스에서는 73라인 이지만 SyntaxHighlighter 설정이 잘 못되어 라인넘버가 밀려서 나옵니다.)

_jspService 메서드는 리턴값이 없으며(void)

인자로 javax.servlet.http.HttpServletRequest 와 javax.servlet.http.HttpServletResponse를 받습니다.

그리고 java.io.IOException, javax.servlet.ServletException 을 throws 할 수 있습니다.

하는 일은

response.setContentType("text/html; charset=UTF-8"); 로 페이지 케릭터 셋을 지정하고

pageContext로 부터 PrintWriter out 을 생성하고 out을 통해 hello, workd 를 출력하고 종료합니다.


이것은 http://localhost 라고 호출 하면 web.xml에 기술되어 있는 welcome-file 에 기술되어 있는 파일중 index.jsp가 호출 되어서 tomcat 내부적으로는 index.jsp에 해당하는 index_jsp.java 서블릿이 실행됩니다.

index_jsp.java 에서는 HTTP GET 메서드에 해당하는 _jspService(...) 메서드가 호출 되면서 파라미터로 사용자의 요청을 담은 HttpServletRequest, 사용자에게 응답할  객체인 HttpServletResponse가 전달 되어 실행 되는 것입니다.

소스한번 살펴 보시고 잘 이해가 안가셔도 한가지만 기억하시면 됩니다.

JSP는 그 자체가 실행되는 것이 아니고 서블릿으로 변환 된 후 실행된다는 것!

이것만 잊지 않으시면 됩니다.


다음시간에는 이것을 참고 해서 hello, world 를 출력하는 서블릿을 작성해 보도록 하겠습니다.

서블릿 개발하기 연재목록

#1 개발환경 구축 (https://opensrc.tistory.com/180?category=475522)

#2 프로잭트 생성 (https://opensrc.tistory.com/181?category=475522)

#3 첫번째 JSP 파일 만들기 (https://opensrc.tistory.com/182?category=475522)

#4 첫번째 서블릿 만들기 (https://opensrc.tistory.com/183?category=475522)

#5 포스트 요청을 처리하는 서블릿 만들기 (https://opensrc.tistory.com/203?category=475522)

#6 GET/POST 요청과 함께 파라메터 전달 하기 (https://opensrc.tistory.com/204?category=475522)

#7 Servlet 들여다 보기 (https://opensrc.tistory.com/206?category=475522)

===========================================================




지난편에서 서블릿 개발을 위한 이클립스 + 톰켓 기반의 개발 환경 설정을 살펴보았습니다.

이번에는 설치한 개발환경에 프로잭트를 생성하고 간단한 서블릿을 만들어서 서버에 올려서 실행하는 것까지 진행 해 보도록 하겠습니다.


1. Dynamic Web Project 생성

이클립스 메뉴 File > New > Other... 선택

[Next >] 클릭


원하는 이름으로 Project name을 입력합니다.

Dynamic web module version이 3.1 인것을 기억하세요.

[Next >] 클릭


Default out folder 를 build\classes => WebContent\WEB-INF\classes 로 변경합니다.

모든 WAS(Web Application Server)에서 Java 소스가 컴파일 되어 배치되야 하는 곳은 Context Root 아래에 WEB-INF\classes 입니다.

아직 생성되지는 않았지만 미리 경로를 잡아주도록 하겠습니다.

[Next >] 클릭


Context root 를 / 로 변경하고, 기본 web.xml 을 생성 하도록 'Generate web.xml deployment descriptor' 를 체크 한다.

하나의 WAS 인스턴스에서 여러개의 Context를 실행하는 경우 Context root 를 각각 다르게 주어야 하지만 우리는 한개만 실행 할 것이르모 / 변경했습니다.

Context root를 특별한 경로로 지정한 경우 브라우져로 접근 할 때 해당 경로를 입력해야 접근 가능합니다.

[Finish] 클릭


2. 프로잭트 Encodeing 변경

전편에서도 언급했지만 최근 Java Web Application은 인코딩이 UTF-8이 기본으로 자리잡은지 오래되었습니다.

이상하게도 윈도우용 이클립스는 최신버전도 2byte 한글을 디폴트로 설정합니다. 나중에 변경해도 큰 문제는 없지만 미리 확인을 하고 넘어가는게 좋습니다.

Project Explorer에서 생성한 프로잭트를 오른쪽 마우스 클릭 > Properties 클릭

좌측에서 Resource 선택, Text file encoding 아래에 Inherited from container (MS949) 에 선택되어 있는 것을 Other로 변경 하고 우측 콤보박스를 UTF-8로 변경

[Apply] 클릭

나머지 내용등은 지금 부터 들여다 볼 필요가 없다고 생각되어 차자 필요할 때 다시 확인하거나 설명 드리도록 하도록 하겠습니다.

2018년 01월 17일 추가

기본 한글이 MS949로 잡히는 이유를 추측해보면... 

윈도우 커맨드창에서 java -XshowSettings:properties -version 명령을 쳐보면

sun.jnu.encoding = MS949

이런 설정이 나옵니다.


UNIX 계열에서 같은 명령을 쳐보면

sun.jnu.encoding = UTF8

이렇게 나옵니다.


저걸 바꿀 수 있는지는 나중에 좀 더 알아 보도록 하겠습니다.


2. 프로잭트 구조 살펴보기

프로잭트가 생성되면 왼쪽 Project Explorer 에서 아래 그림과 같은 구조가 보입니다.

다른 것들은 차차 설명하기로 하고

중요한 부분은

Java Resources > src : Java 클래스의 Source가 패키지 구조에 마춰 배치 됩니다.

Java Resources > Libraries : 추가된 라이브러리들이 표시됩니다.

WebContent : 위 설정에서 Context root 와 매핑해준 WebContent 입니다.

외부에서 http://localhost/~ 로 접근 되는 위치입니다.

WebContent\WEB-INF : Java Web Applicatin 에서 꼭 필요한 디렉토리 입니다. 여기에 web.xml도 있고 컴파일된 java 클래스들이 배치되는 classes 디렉토리도 있고 외부 라이브러리들이 배치되는 lib 디렉토리도 여기에 존재 합니다.

WebContent\WEB-INF\web.xml : 향후 우리가 친숙해져야 할 파일입니다. Java 기반의 Web Applicatiaon의 기본적인 특성을 여기에 모두 기술 합니다. 새로 생성하는 서블릿역시 여기에 등록 하고 외부에서 접근할 URI를 매칭해줘야 합니다.

프로잭트를 생성 할 때 마지막에 Generate web.xml deployment descriptor를 체크 해주었기 때문에 기본적인 내용이 기술된 web.xml이 생성되어 있습니다.

현재는 Servlet 3.1 스펙을 이용하겠다는 표시로 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd 내용 정도만 확인하시면 될 것 같습니다.


이정도만 이해하시고 나머지는 차자 기회가 되면 설명하도록 하겠습니다.


3. 서버 설정

Java Web Application을 실행하기 위해서는 Java Web Application Server가 필요합니다. 줄여서 WAS 라고 부르기도 하고 톰켓같은 경량서버는 Servlet Container라고 부르기도 합니다.

우리는 이미 전 회차에서 Apache Tomcat v8.0을 사용하기 위해 다운로드 받아놓았습니다.

그것을 우리가 만든 프로잭트에서 사용할 수 있도록 설정을 해보겠습니다.

이클립스 메뉴 Window > Show view > Other... 클릭

Server > Servers 선택

[Open] 클릭


하단의 Servers 텝에서

No Server are available. Click this link to create a new server... 를 클릭합니다.


이전 회자에서 다운받은 Tomcat 8.0이 기본적으로 선택되어 표시됩니다.

[Next >] 클릭


우리가 생성한 프로잭트가 좌측에 표시됩니다.

더블클릭 하거나 [Add >] 버튼을 클릭하면 아래 화면처럼 우측으로 이동합니다.

[Finish] 클릭


아래 그림처럼 하단의 Servers 텝에 Tomcat 서버가 사용할 수 있는 상태로 활성화 되었습니다.

Project Explorer 에도 우리가 추가한 프로잭트 외에 Servers 트리가 하나 생겼습니다.


서버 설정을 변경하기 위해서 Servers 텝에서 추가한 서버를 더블클릭 하여 서버 설정을 확인합니다.


General Information 섹션에 Open launch configuration 클릭


맨마지막 Common 텝을 선택하고

Encoding을 Default - inherited (MS949) 에서

Other 로변경하고 UTF-8로 선택합니다.

[Apply] , [OK] 클릭


아래 그림처럼

① HTTP/1.1 Port를 : 8080 -> 80으로 변경한 후 

② Save (Alt + S)

③ Close

후에 콘솔을 확인하기 위해 아래처럼 Servers 텝을 Project Explorer 텝 아래로 드래그 해서 옮기면 편하게 사용 할 수 있습니다.(선택사항)


4. 서버 실행

톰켓 디버그 모드로 실행 버튼

톰켓 실행 버튼

개발 할 때는 디버그 모드로 사용하는게 편리합니다.

디버그 모드로 실행하도록 하겠습니다.


하단에 console 텝이 생기면서

붉은색 로그들이 주르륵 나옵니다.

맨마지막에 

정보: Server startup in 3340 ms

이 메시지가 나오면 정상적으로 톰켓이 가동된 것입니다.


아직 아무것도 만들지 않았기 때문에

브라우저에서 http://localhost 라고 입력하면 404 Not Found 에러가 발생합니다.

Stop 버튼을 눌러서 종료 하거나 위에서 설명한 디버그, 시작 버튼을 눌러 재시작 할 수 있습니다.



다음 회에서는 간단한 jsp 파일과 servlet 파일을 만들어서 호출해 보도록 하겠습니다.

서블릿 개발하기 연재목록

#1 개발환경 구축 (https://opensrc.tistory.com/180?category=475522)

#2 프로잭트 생성 (https://opensrc.tistory.com/181?category=475522)

#3 첫번째 JSP 파일 만들기 (https://opensrc.tistory.com/182?category=475522)

#4 첫번째 서블릿 만들기 (https://opensrc.tistory.com/183?category=475522)

#5 포스트 요청을 처리하는 서블릿 만들기 (https://opensrc.tistory.com/203?category=475522)

#6 GET/POST 요청과 함께 파라메터 전달 하기 (https://opensrc.tistory.com/204?category=475522)

#7 Servlet 들여다 보기 (https://opensrc.tistory.com/206?category=475522)

===========================================================



0. 기본 작업 환경

Windows 10 Pro 64bit

JDK 1.8 64Bit

Eclipse Java EE IDE for Web Developers. v4.X 64bit (Oxygen)

Apache Tomcat 8.0

Servlet 3.1

기준으로 작업 합니다.

버전이 약간씩 다르더라고 본인의 환경에 맟춰서 선택하면 큰 문제는 없습니다.


1. JDK 다운로드

http://www.oracle.com/technetwork/java/javase/downloads/index.html

현재 Java 9 (JDK 1.8) 가 나온 시점이지만 우리는 JDK 1.8 기준으로 작업 하기로 합니다.

전통적으로 상용 서비스에서는 최신버전 보다는 많은 래퍼런스가 있고 안정화된 버전을 선호하는 편입니다.

개인적인 생각으로는 혼자 스터디하는 것은 스스로 최신 스팩을 접하면서 남들보다 한발 앞서가는 것도 좋은 자세라고 생각합니다.


2. 이클립스 다운로드

https://www.eclipse.org/downloads/eclipse-packages/

본인의 환경에 맞는 것을 선택해서 원하는 위치에 다운로드하고 압축을 해제 합니다.


본 예제에서는 Eclipse Java EE IDE for Web Developers. ver 4.7.1a 64bit (Oxygen)을 이용 합니다.

2019년 4월 현재 Oxygen 버전을 다운 받을 수 있는 링크는

https://www.eclipse.org/downloads/packages/release/oxygen/r

입니다.

이클립스는 자주 신버전이 나오기 때문에 꼭 Oxygen 이 아니더라도 다른 버전을 사용해도 큰 문제는 없지만

패키징은 꼭 Eclipse IDE for Java EE Developers 를 선택 하셔야 합니다.

패키지 설명을 보면 

"Tools for Java developers creating Java EE and Web applications, including a Java IDE, tools for Java EE, JPA, JSF, Mylyn, EGit and others."

이렇게 되어 있습니다. Web applications 라는 설명이 들어가 있는 패키지를 다운 받아야 합니다.

그냥 Eclipse IDE for Java Developer를 선택하시면 Java Applicaiton을 개발 할 수 있는 기본 환경만 들어있어서 WTP(Web Tools Platform) 같은 Web 개발 도구를 추가 설치 해야 합니다.


이클립스를 처음 실행하고 원하는 위치의 작업공간(workspace)을 설정 하시기 바랍니다.

저의 경우 C:\work 아래에 년월_프로잭트명 으로 디렉토리를 만들고 그 아래에 모든 프로잭트에 관련된 디렉토리를 배치합니다.

프로잭트가 끝나고 더이상 소스를 열어볼 필요가 없어지면 통째로 압축하여 백업하였다가 필요 할 때 C:\work 폴더에 압축을 해제 하면 다시 프로잭트의 내용을 살펴 볼 수 있습니다.


3. 서버 다운로드

Servlet을 실행하기 위해서는 JSP, Servlet 스펙을 만족하는 어플리케이션 서버를 설치 해야 합니다. 프로잭트를 생성하면서 설치하는 것도 가능하지만 미리 원하는 서버를 설치하도록 하겠습니다.

Eclipse 메뉴 Window > Preferences 선택

왼쪽 트리에서 Server > Runtime Environments 선택

오른쪽의 [Add...] 버튼을 클릭합니다.


이 예제예서는 Apache Tomcat v8.0을 이용 할 것 입니다.

Apache Tomcat v8.0을 선택 후 

[Next] 클릭


서버를 다운로드 받을 위치를 지정합니다.

[Browse...] 클릭


원하는 위치에 다운로드 받을 디렉토리를 생성합니다.


디렉토리를 선택하면 상단에 붉은색 X마크와 함께 'Unknown version of Tomcat was specified.' 라고 표시됩니다.

[Download and Install...] 버튼을 클릭


라이센스 동의

'I accept the terms of the license agreement' 선택 후 [Finish] 클릭


이클립스 하단 상태바에 Installing server runtime envrionment 라고 표시되고 다운로드 상태가 표시 됩니다.


이클립스 하단 우측에 상태바를 보면 다운로드 중인 것을 알 수 있습니다.


다운로드가 완료 되면 붉은색 X마크가 사라지고 'Specify the Installation directory' 로 변경됩니다.

[Finish] 클릭


톰켓 서버가 추가된 상태


4. 한글 설정 확인

우리가 개발할 소스의 모든 한글 인코딩은 UTF-8 로 통일할 것입니다. 요즘 거의 모든 프로젝트의 인코딩은 UTF-8로 통일된 것 같은데 아직도 윈도우즈용 이클립스를 처음 설치하면 아직도 EUC-KR 방식으로 기본 세팅이 되는 것 같습니다.

이클립스 메뉴 Window > Preferences > General > Workspace > Text file encoding : UTF-8 선택 후 [Apply]

이클립스 메뉴 Window > Preferences > General > Content Types > Text > Default encoding : UTF-8 입력 후 [UPDATE] 클릭

이클립스 메뉴 Window > Preferences > General > Content Types > Text > JSP > Default eocoding : UTF-8 입력 후 [UPDATE] 클릭

이클립스 메뉴 Window > Preferences > General > Content Types > Text > Java Source File > Default eocoding : UTF-8 입력 후 [UPDATE] 클릭

이클립스 메뉴 Window > Preferences > General > Content Types > Text > Java Properties File > Default eocoding : UTF-8 입력 후 [UPDATE] 클릭

Eclipse 메뉴 Wondow > Preferences > Web > CSS Files > Encoding ▶ ISO 10646/Unicode(UTF-8) 선택후 [Apply]

Eclipse 메뉴 Wondow > Preferences > Web > HTML Files > Encoding ▶ ISO 10646/Unicode(UTF-8) 선택후 [Apply]

Eclipse 메뉴 Wondow > Preferences > Web > JSP Files > Encoding ▶ ISO 10646/Unicode(UTF-8) 선택후 [Apply]


이외에 서버의 URLEncoding 등은 프로잭트 생성 후 확인하도록 하겠습니다.


이제 기본적으로 서블릿을 개발하기 위한 준비가 되었습니다.

다음 편에서는 프로잭트를 생성해보도록 하겠습니다.

        $scope.test1 = {

        label : "test"

        }

        

        $scope.test2 = {};

        $scope.test3 = {};

        

        test();

        

        function test() {

        console.log("-------------------------");

        testPrint();

        testCopy();

        console.log("-------------------------");

        testPrint();

        console.log("-------------------------");

        $scope.test1.label = "TEST";

        testPrint();

        }

        

        function testCopy() {

        $scope.test2 = $scope.test1;

        angular.copy($scope.test1,$scope.test3);

        }

        

        function testPrint() {

        console.log("$scope.test1");

        console.log($scope.test1);

        console.log("$scope.test2");

        console.log($scope.test2);

        console.log("$scope.test3");

        console.log($scope.test3);

       

        }




결과

-------------------------
$scope.test1
{label: "test"}
$scope.test2
{}
$scope.test3
{}
-------------------------
$scope.test1
{label: "test"}
$scope.test2
{label: "test"}
$scope.test3
{label: "test"}
-------------------------
$scope.test1
{label: "TEST"}
$scope.test2
{label: "TEST"}
$scope.test3
{label: "test"}


'Html-JavaScript' 카테고리의 다른 글

Vue.js 기본 구조  (0) 2024.02.28
스타일로 제목 자르기  (0) 2011.05.23
클립보드에 복사  (0) 2008.10.15
문자열 length구하기  (0) 2008.07.22
웹에서 이미지의 특정색을 투명하게 처리  (0) 2007.10.16

/**

* 한글 조사(助詞) 연결 (을/를,이/가,은/는,로/으로)

  * 한글에 단어를 연결하는 조사(助詞) 는 앞에 오는 체언(體言)에 따라 변화되는 경우에 사용

  * 규칙

* 1. 체언의 종성에 받침이 있는 경우 '을/이/은/으로/과'

* 2. 체언의 종성에 받침이 없는 경우 '를/가/는/로/와'

* 3. ‘로/으로'의 경우 체언 종성의 받침이 ‘ㄹ' 인경우 '로’ 아니면 '으로’

* @param name

* @param firstValue

* @param secondValue

* @return

*/

public static String getPostWord(String name, String firstValue, String secondValue) {

/*

* 사용법

String name;

 

name = "네이버";

System.out.println(CommonUtils.getPostWord(name,"으로","로"));

System.out.println(CommonUtils.getPostWord(name,"을","를"));

System.out.println(CommonUtils.getPostWord(name,"이","가"));

System.out.println(CommonUtils.getPostWord(name,"은","는"));

System.out.println(CommonUtils.getPostWord(name,"과","와"));

 

name = "카카오톡";

System.out.println(CommonUtils.getPostWord(name,"으로","로"));

System.out.println(CommonUtils.getPostWord(name,"을","를"));

System.out.println(CommonUtils.getPostWord(name,"이","가"));

System.out.println(CommonUtils.getPostWord(name,"은","는"));

System.out.println(CommonUtils.getPostWord(name,"과","와"));

 

name = "구글";

System.out.println(CommonUtils.getPostWord(name,"으로","로"));

System.out.println(CommonUtils.getPostWord(name,"을","를"));

System.out.println(CommonUtils.getPostWord(name,"이","가"));

System.out.println(CommonUtils.getPostWord(name,"은","는"));

System.out.println(CommonUtils.getPostWord(name,"과","와"));

 

name = "문화포털";

System.out.println(CommonUtils.getPostWord(name,"으로","로"));

System.out.println(CommonUtils.getPostWord(name,"을","를"));

System.out.println(CommonUtils.getPostWord(name,"이","가"));

System.out.println(CommonUtils.getPostWord(name,"은","는"));

System.out.println(CommonUtils.getPostWord(name,"과","와"));

*/

 

try {

char lastName = name.charAt(name.length() - 1);

// 한글의 제일 처음과 끝의 범위밖일 경우는 오류

if (lastName < 0xAC00 || lastName > 0xD7A3) {

   return name;

}

 

int lastCharIndex = (lastName - 0xAC00) % 28;

 

// 종성인덱스가 0이상일 경우는 받침이 있는경우이며 그렇지 않은경우는 받침이 없는 경우

if(lastCharIndex > 0) {

// 받침이 있는경우

// 조사가 '로'인경우 'ㄹ'받침으로 끝나는 경우는 '로' 나머지 경우는 '으로'

if(firstValue.equals("으로") && lastCharIndex == 8) {

name += secondValue;

} else {

name += firstValue;

}

} else {

// 받침이 없는 경우

name += secondValue;

}

} catch (Exception e) {

//e.printStackTrace();

}

 

return name;

}

 

 

https://jeonghakhur.gitbooks.io/how-to-debug-frontend/content/chapter3.html

■ 날짜 관련 postfix

DATE 타입 : _DATE

예 ) REG_DATE DATETIME NOT NULL

TEXT 변환 타입 : _DT

예) BIRTH_DT VARCHAR(10) DEFAULT '2016-01-01' NOT NULL

/* 현재월 마지막 ?요일 날짜 구하기  */

SELECT 

    CASE WHEN DDAY < 0 THEN LAST_DAY(SYSDATE) -7 + DDAY * -1

             ELSE LAST_DAY(SYSDATE) - DDAY

             END AS DD

FROM (

    SELECT

        /* 원하는 요일 상수

           일요일:1

           월요일:2

           화요일:3

           수요일:4

           목요일:5

           금요일:6

           토요일:7

        */

        TO_NUMBER(TO_CHAR(LAST_DAY(SYSDATE),'D')) - 4 AS DDAY /* 4=수요일 */

    FROM DUAL

);



+ Recent posts