2019년 5월 29일 수요일

Spring 3.X 프로젝트 설정하기 (xml 방식)

Spring 버전 3의 프로젝트 설정방법에 대해 알아보기로 한다. 버전 3 이후론 Annotation을 사용해 코드 내에서 대부분의 설정을 할 수 있지만 이번 포스팅에선 xml 설정 방식을 따르기로 한다. IDE는 Eclipse 기준이고 Spring 3.X, Maven, Mybatis를 쓴다고 가정하였다.

당연히 Spring Boot를 쓰거나 프로젝트 템플릿을 내려받아 쓰는게 편할 것이다. 그렇지만 편해지기 이전에 어떻게 프로젝트 설정을 하고, 설정된 프로젝트는 어떻게 켜지는지 알 필요가 있다고 생각한다. Spring관 관련이 적지만 아래의 링크가 도움이 되었다. 아래 링크는 Spring 프레임워크 없이 쌩 Java-웹 표준 사양만 가지고 웹 프로젝트를 동작시키는 것에 대해 잘 설명해주고 있다.
https://www.in28minutes.com/first-java-web-application-with-jsp-and-servlets-in-25-steps

프로젝트를 만들어보자.


1. File > New > Maven Project

- Use default Workspace location
- maven-archetype-webapp
- Group Id: 네이밍 스키마를 적용한 고유 도메인 이름. 예: org.apache.maven, org.apache.commons
- Artifact Id: 프로젝트 이름, jar 또는 war 파일의 이름이 된다.
# Group Id와 Artifact Id는 특수문자를 사용하지 않고 가급적 소문자로 작성한다. 예: maven, commons-math


2. Java 버전, Facet 설정 [Optional]

- Properties > Java Build Path > Libraries
- Properties > Java Compiler
# pom.xml에서 지정해도 괜찮다. (maven-compiler-plugin)

- 프로젝트 경로 > .settings > org.eclipse.wst.common.project.facet.core.xml
 <installed facet="java"/>, <installed facet="jst.web"/> (Dynamic Web Module -> 3.1) 버전 설정.
- File > Restart
- Properties > Project Facets > 표시되는 버전이 xml에 지정한 것과 일치하는지 확인.


3. web.xml - 스키마 버전 세팅 [Optional]

# 버전이 높을수록 사용할 수 있는 태그의 표현 범위는 늘어나겠지만 안해줘도 크게 상관은 없는듯.

<?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"
    version="3.1">
    <display-name>project name</display-name>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>


4. Maven Install & edit pom.xml

- Run As > Maven install
- dependency와 build 항목에 필요한 설정을 해준다. (web에 일반적으로 쓰이는 pom.xml을 찾아 참고하면 된다.)
# dependency : spring-context, spring-webmvc, ...
# build : maven-eclipse-plugin, maven-compiler-plugin, maven-war-plugin
- Spring Tools > Add Spring Project Nature : 'Spring Elements'를 프로젝트에 추가해준다.
- Maven > Update Project : pom.xml 내용을 프로젝트에 적용해준다.


5. web.xml, servlet-context.xml 설정

- web.xml에 FrontController 역할을 하는 org.springframework.web.servlet.DispatcherServlet을 등록해준다.
# DispatcherServlet은 보통 하나만 등록하지만 하나의 프로젝트 내부에서 성격이 다른 여러 개의 서비스를 서빙해야 하는 경우 여러 개를 등록할 수 있다.
# DispatcherServlet의 contextConfigLocation 파라미터에 서블릿(스프링) 관련 context 경로를 잡아준다.
- /WEB-INF/config/spring/*-context.xml과 같이 설정할 수 있다.

<servlet>
    <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/spring/*-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

- /WEB-INF/config/spring/servlet-context.xml을 생성한다. 파일 형식은 Spring Bean Configuration File. 네임스페이스 beans, context, mvc 체크.
- 생성된 servlet-context.xml에 component-scan을 위한 base package를 지정해 준다.
- 애플리케이션 내에서 @Controller, @Component 등 프레임워크에서 제공되는 annotation을 이용하는 것이 일반적이므로 <mvc:annotation-driven />을 선언한다.
- view가 있는 프로젝트라면 view resolver를 설정해준다.
- web.xml에서 encoding filter를 설정해준다. 로그 출력을 위한 log filter, 크로스 도메인간 요청 허용을 위한 cors filter도 필요하면 등록시켜준다.

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


6. applicationContext.xml 설정

- applicationContext.xml을 생성한다. 형식은 Spring Bean configuration File. 네임스페이스 aop, beans, p, tx 체크.
- web.xml에서 contextConfigLocation 파라미터의 값이 applicationContext.xml을 가리키도록 한다.
- web.xml에서 listener로 ContextLoaderListener를 선언한다.


<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

- applicationContext.xml에 필요한 bean을 등록해준다. ContextLoaderListener가 applicationContext.xml에 선언된 bean을 파싱하고 singleton으로 로드시키는 역할을 하나보다. Application context와 Servlet context가 있고, 사용자는 이 둘에 대한 life cycle을 Spring Framework에 위임하게 되는 셈.

더 적으려다가 아래 링크를 참고하는게 나을 것 같다. :(
https://handcoding.tistory.com/10?category=621283

2019년 5월 15일 수요일

Python 가상 환경 사용하기 (venv)

Python 3.3 이후 버전부터는 venv 모듈을 이용해 가상 환경을 생성한다. (아래 버전에서는 virtualenv 패키지를 이용하면 된다.) venv 모듈을 통해 특정 프로젝트가 포함된 디렉터리를 Global 환경과 격리시킬 수 있고, 독립적으로 설치된 Python 패키지 집합을 운영할 수 있게 된다.

python -m venv my-env # my-env는 가상 환경 이름이다.
call my-env\Scripts\activate # activate.bat를 호출함으로써 가상 환경이 활성화 된다.
...
(my-env) pip install -r requirements.txt

왜 필요한가?

Python으로 작성된 여러 개의 프로젝트를 운영하는 경우 프로젝트 상황에 따라 같은 모듈이더라도 참조 버전이 갈리는 경우가 발생한다. 혼자서 하는 작업이라면 버전이 높은 쪽을 기준으로 소스 코드를 수정하면 되겠지만 여럿이서 하는 작업이라면 곤란해진다.

이런 문제점의 대안으로 Python은 가상 환경(Virtual environment)이라는 개념을 제공하는데, 프로젝트 별로 전용의 Environment를 설정할 수 있게 해줌으로써 버전 충돌로 인한 문제점을 해결해주고 있다. 특히 참조하는 모듈의 버전 뿐만 아니라 Python 인터프리터 버전도 설정할 수 있기 때문에 여러모로 편리하다고 할 수 있다.

https://dojang.io/mod/page/view.php?id=2470

2019년 5월 2일 목요일

국제화(I18N)과 현지화(L10N)

국제화 (Internationalization): 텍스트의 글로벌한 입출력 처리에 관한 작업. 다양한 나라의 언어 표현 방법을 모두 수용할 수 있도록 만들어주는 것을 의미한다. (=다국어 처리 / Unicode)

현지화 (Localization): 애플리케이션을 특정 Locale에 적합하게 만드는 작업.
 * Locale: 시간대, 시간 및 날짜 표기 형식, 숫자 표기 형식, 통화 기호 등.

국제화는 줄여서 I18N(Internationalization은 I로 시작해서 N으로 끝나는 18자리 단어.)
현지화는 줄여서 L10N(마찬가지로 Localization은 L로 시작해서 N으로 끝나는 10자리 단어.)

국제화는 주로 언어적인 내용과 관련이 있지만 현지화는 지역의 관습이라든가 정렬의 순서, 미적 감각 등 보다 섬세한 측면에서 문화적인 내용을 포함한다.

https://ko.wikipedia.org/wiki/%EA%B5%AD%EC%A0%9C%ED%99%94%EC%99%80_%EC%A7%80%EC%97%AD%ED%99%94

2019년 4월 28일 일요일

OEE (설비 종합 효율)


OEE = Overall equipment effectiveness


OEE는 제조 공정이 얼마나 효율적으로 수행되는지를 평가하기 위한 지표다. OEE 수준은 산업 별로 다르게 나타날 수 있으므로 절대적인 척도가 아니며 스스로의 과거 수준과 현 수준을 파악하는데 더 유용하다. OEE의 계산은 어렵지 않지만 metric에 대한 표준에 대해 주의를 기울일 필요가 있다.

OEE의 기본 측정 항목
- Availability : 계획된 생산 시간 대비 작업 수행 시간. (Uptime)
- Performance : 설계된 생산 속도 대비 실제 속도
- Quality : 양품율. First Pass Yield.

* 참고로 OEE에 Loading을 곱하면 TEEP(Total effective equipment performance)가 된다.
Loading = Scheduled Time / Calendar Time. (7일 중 5일을 일한다면. Loading은 5/7 = 71.4%)

OEE 식.
OEE = Availability * Performance * Quality
* 예시: (OEE=73.6%) = (Availability= 86.6%)*(Performance=93%)*(Quality=91.3%)
* Availability = Operating time / Scheduled time. (* 점심 시간과 같은 계획된 정지 시간은 빼야 한다.)
* Performance = (Parts produced * Ideal cycle time) / Operating time
* Quality = (Units produced - defective units) / (Units produced)

아니면 단순하게 이런 식의 계산도 가능하다.
1. 28,800초 동안 14,400개 생산. → 하나 만드는데 2초.
2. 최적 싸이클 타임이 1.5초인데 이 경우 14,400개 만드는덴 21,600초만 필요함. (7,200초의 Loss가 있었음.)
3. OEE = 21,600 / 28,800 = 75%

3개의 metric을 단순하게 곱해버리기 때문에 하나만 깨져도 결과가 좋지 않게 나오는 것을 볼 수 있다. 따라서 모델이 해당 산업군에 적합한지 부터 따져볼 필요가 있겠다.

-

* 조업시간 = 생산을 해야 되는 시간 (휴무 제외)
* 부하시간 = 설비를 가동해야 하는 시간 (계획가동정지 제외)
* 가동시간 = 설비가 실제 가동한 시간 (정지로스 제외)
* 정미가동시간 = 제품을 만든 시간
* 가치가동시간 = 양품을 만든 시간

2019년 4월 17일 수요일

AJAX와 CORS

AJAX (Asynchronous JavaScript And XML)


AJAX는 서버와 통신하기 위해 XMLHttpRequest 객체를 사용하는 것을 의미한다. 이 객체를 이용해 XML, JSON, HTML 그리고 일반 텍스트 형식 등을 포함한 다양한 포맷을 주고 받을 수 있다. 페이지 전체를 리프레쉬하지 않고서도 수행되는 "비동기성"은 AJAX의 가장 큰 특징으로 페이지의 일부분만을 업데이트 할 수 있게 해준다.

AJAX와 함께 자주 거론되는 개념이 있는데 그건 바로 CORS다. CORS의 도입 배경은 다음과 같다. HTTP 요청은 기본적으로 A 도메인에서 B 도메인으로 향하는 cross-site HTTP 요청이 가능하지만 <script></script>로 둘러싸인 스크립트에서 생성된 cross-site HTTP 요청은 보안상 이유로 호출이 제한된다. XMLHttpRequest도 same-origin 정책을 따르는데 XMLHttpRequest에 대해서도 cross-site HTTP 요청이 가능해야 한다는 요구가 생겨 W3C에 의해 CORS 권고안이 나오게 되었다.



CORS (Cross-Origin Resource Sharing)


CORS는 origin 도메인에서 실행중인 웹 애플리케이션이 추가 HTTP 헤더를 사용해 브라우저가 다른 출처 서버의 자원에 액세스할 수 있는 권한을 가지도록 하는 메커니즘이다.
→ CORS를 쓰면 AJAX로도 다른 도메인의 자원을 쓸 수 있다.

1. CORS 스펙에선 서버 데이터에 side-effects를 일으킬 수 있는 요청 메서드(특정 MIME 유형의 POST 등)에 대해 브라우저가 요청할 내용을 OPTIONS 메서드를 이용해 사전 전달(preflight) 하도록 하는데, 이는 서버로부터 지원 중인 메서드(GET, POST, ...) 목록을 내려 받은 뒤 승인(approval) 시에 실제 HTTP 요청을 전송하는 것을 말한다.
→ 사용자가 요청 프로세스를 직접 다루는 것이 아니라 브라우저가 알아서 처리한다.
2. 서버는 클라이언트에게 요청과 함께 자격 증명(credential)을 보내야하는지 여부를 알릴 수도 있다.


AJAX 요청의 흐름.

기본적으로 비동기이므로 요청 전에 서버로 보낸 요청에 대한 응답을 받았을 때 처리할 함수를 미리 지정해주어야 한다.
onreadystatechange property에 지정하면 된다.
그리고 나서 open()과 send()를 차례대로 요청하면 된다.


httpRequest.onreadystatechange = function () {
  // ...
};

// 쓸 일은 없겠으나 open의 셋째 파라미터를 false로 지정하면 동기적으로 동작한다.
// 이 경우엔 onreadystatechange는 필요하지 않다.
httpRequest.open('GET', 'http://www.example.org/some.file', true); 

// send()의 파라미터가 null인 까닭은 'GET' 방식의 호출이기 때문이다. 'POST'인 경우엔 서버로 보낼 데이터를 send() 파라미터에 지정하면 된다.
// 다만 서버가 데이터의 형식을 알 필요가 있기 때문에 request에 MIME type을 먼저 설정해야 한다.
httpRequest.send(null);
httpRequest.setRequestHEader('Content-Type', 'application/x-www-form-urlencoded');

위와 같이 서버 응답에 대한 처리는 onreadystatechange에 지정한 함수에서 이루어진다.
일반적으로 httpRequest.readyState의 값이 XMLHttpRequest.DONE(4) 이면서 httpRequest.status의 값이 200 OK인 경우 호출-응답의 프로세스가 성공한 것이다.

받은 데이터는 responseText 또는 responseXML property로 읽어오면 된다. responseXML의 경우 서버의 응답을 XMLDocument 객체로 반환하며 DOM API를 통해 이 객체를 다룰 수 있을 것이다.

기본적인 예제는 아래를 참고하자.

<button id="ajaxButton" type="button">Make a request</button>

<script>
(function() {
  var httpRequest; // httpRequest 변수는 지역 변수로 선언해야 한다. 
                   // 전역으로 사용되면 여러 요청에 의한 경쟁 상태(race condition)이 발생할 수 있다.
  document.getElementById("ajaxButton").addEventListener('click', makeRequest);

  function makeRequest() {
    httpRequest = new XMLHttpRequest();

    if(!httpRequest) {
      // Can not create instance.
      return false;
    }
    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('GET', 'test.html');
    httpRequest.send();
  }

  function alertContents() {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        alert(httpRequest.responseText);
      } else {
        // Error
      }
    }
  }
})();
</script>

https://developer.mozilla.org/ko/docs/Web/Guide/AJAX/Getting_Started
https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS#%EA%B0%9C%EC%9A%94

2019년 4월 15일 월요일

WPF - Binding Proxy

WPF에서 DataContext 속성은 할당된 요소의 자식에게 자동으로 상속되므로 편리하다. 따라서 바인딩하려는 각 요소에 대해 다시 DataContext를 지정하지 않아도 된다.

그러나 WPF에서 바인딩 대상이 Target Object와 다르거나 Visual Tree가 다른 경우 참조식을 작성할 때 불필요하게 식이 길어지는 경우가 종종 있다. (DataGrid, ItemsControl 내 엘리먼트가 ViewModel의 프로퍼티를 참조하는 경우)

이런 상황에 놓이면 사용자가 해당 요소에 DataContext를 직접 알려주어야 한다.
이러한 경우 Binding Proxy를 만들어 이용하면 코드도 간결해지고 표현도 명확해진다.

public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy));
}

Freezable을 구현하는 까닭은 WPF에서 Freezable 객체는 Visual Tree 또는 Logical Tree가 다른 경우에도 DataContext를 상속할 수 있기 때문이다. 사용법은 아래와 같은데, proxy 객체의 Data attached 프로퍼티에 바인딩 된 뷰모델을 바로 참조할 수 있다. (물론 단순한 경우엔 x:Name ~ x:Reference, RelativeSource ~ FindAncestor가 나을지도 모르겠다.)


    <DataGrid>
        <DataGrid.Resources>
            <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
                                                Source={StaticResource proxy}}"/>
        </DataGrid.Columns>
    </DataGrid>


https://thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/