・이클립스의 project에서 오른쪽 마우스 클릭 후 build path를 클릭하면 다음과 같은 콘솔이 나온다.

・이 콘솔이 의미하는 바는, 당 프로젝트에서 참조하고 있는 라이브러리들을 나타내는 것이다.

・당연히 자바 기본 라이브러리는 포함이 되어 있고(FRE system library), 웹프로젝트 이므로 Apach-Tomcat도 디펜던시로서 포함되어 있다.

・또 프로젝트 생성시 자동으로 추가되는 라이브러리들 외에도 오른쪽 클릭버튼 들을 보면 external jar을 추가 할 수 있는데, Add Library를 클릭하면 된다.







+이클립스에서 톰캣 서버 설정하기


・웹 프로젝트를 생성하고 싶을 땐 리본메뉴 - Window - preference - Server - Runtime Environment - Add 클릭 후

톰캣을 설치한 절대경로를 설정해주면 된다. 












・아파치 톰캣의 라이프러리를 오픈해봤다.

・보통 아파치 톰캣의 라이브러리는 C:\apache_tomcat\apache-tomcat-8.5.23\lib 에 존재하고 톰켓에 추가할 라이프러리가 생기면 jar파일을 톰캣 라이브러리 디렉토리에 추가하면 된다.







・기본 자바 jre의 라이브러리의 리스트이다.





ajax json 형식을 자바에서 파싱하기

var insertArray=[];

var updateArray=[];

var deleteArray=[];

$.each(dataSet,function(index,m){

switch(m.state){

case 'insert' :  insertArray.push(m); break;

case 'update' :  updateArray.push(m); break;

case 'delete' :  deleteArray.push(m); break;

}

});

var sendObject={"insert":insertArray,"update":updateArray,"delete":deleteArray};

var sendMsg=$.toJSON(sendObject);    //키 값 키 값 ... 을 문자열로 바꿔준다. 예)"{'insert':insertArray}"


//jQuery ajax 방식으로 json2,jsp로 data에 msg라는 키와 문자열로된 값을 전송한다.

$.ajax({

url:'json2.jsp',

data:{msg:sendMsg},

success:function(data){    //전송이 성공적이면 여기 소스가 실행된다.

},

error:function(request,status,error){ alert(request+","+status+","+error); }    //전송이 실패되면 여기 소스기 실행된다.

});


<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>

<%@ page import="java.util.ArrayList" %>

<%@ page import="java.util.HashMap" %>

<%@ page import="net.sf.json.JSONObject"%>

<%@ page import="net.sf.json.JSONArray"%>  

<%@ page import="com.seoulit.member.model.MemberDAO" %>

<%@ page import="com.seoulit.member.model.Member" %>

<%

String string=request.getParameter("msg");

JSONObject jsonobject=JSONObject.fromObject(string);    //문자열을 jsonobject형식으로 바꿔준다.

JSONArray insertArray=jsonobject.getJSONArray("insert");    //보낼 때 배열로 보냈기 때문에 키를 주면 JSONArray로 받는다.

JSONArray updateArray=jsonobject.getJSONArray("update");

JSONArray deleteArray=jsonobject.getJSONArray("delete");

ArrayList<Member> insertArrayList=new ArrayList<Member>();

ArrayList<Member> updateArrayList=new ArrayList<Member>();

ArrayList<Member> deleteArrayList=new ArrayList<Member>();

for(int i=0; i<insertArray.size(); i++){

JSONObject obj=insertArray.getJSONObject(i);    //배열을 다시 object에 담고

insertArrayList.add((Member)JSONObject.toBean(obj,Member.class));    //toBean에 객체와 Member클래스를 넘겨주면Member객체로 바꿔준다.

}

for(int i=0; i<updateArray.size(); i++){

JSONObject obj=updateArray.getJSONObject(i);

updateArrayList.add((Member)JSONObject.toBean(obj,Member.class));

}

for(int i=0; i<deleteArray.size(); i++){

JSONObject obj=deleteArray.getJSONObject(i);

deleteArrayList.add((Member)JSONObject.toBean(obj,Member.class));

}

MemberDAO dao=MemberDAO.getInstance();

dao.insertMemberList(insertArrayList);

dao.updateMemberList(updateArrayList);

dao.deleteMemberList(deleteArrayList);

%>



출처: http://smartdresser.tistory.com/12 [먿쨍이공간]


by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaru, http://www.jiniya.net


컴퓨터에서 무엇인가를 동시에 실행시킨다는 개념은 초창기부터 있었다. 실제로 1970년대 유닉스 구현에는 이미 멀티태스킹이라는 개념이 포함되어 있었다. 하지만 이런 방식이 범용적으로 사용되기 까지는 오랜 시간이 걸렸다. 1995년 Windows 95가 나오기까지 PC 운영체제를 거의 독점했던 DOS 시절에는 한번에 하나의 프로그램이 실행되는 것이 상식이었고, 다른 프로그램과 동시에 실행되는 프로그램을 만드는 것이 대단한 테크닉으로 취급 받았다. Windows 3.1의 등장과 함께 멀티태스킹이란 개념이 일반인들에게 본격적으로 소개되었다. Windows 3.1은 DOS 위에서 구동되는 프로그램이었지만 여러모로 현재 Windows의 기틀을 다진 운영체제였다. 비선점형 멀티태스킹 방식을 지원했고 현재 사용되는 GUI의 기본 개념을 소개한 운영체제였다. 이런 시기를 거쳐 Windows 95가 등장하면서 DOS 시절에 굳어졌던 일반인들의 상식은 완전히 뒤집혔다. 이제 프로그램은 원래 당연히 동시에 실행되는 것이고, 운영체제에서 멀티태스킹이란 당연히 지원해야 되는 기능이 돼버렸다. Windows 95의 선점형 멀티태스킹은 일반인들에게 그런 인식을 심어주기에 충분했다.


하지만 모든 일이 항상 좋은 점만 있는 것은 아니다. 일반인들에게 당연한 기능 내지는 축복이 시선을 달리해서 개발자의 입장으로 바라보면 재앙과도 같기 때문이다. 단순히 동시에 실행된다는 사실 만으로는 그렇게 많은 문제가 발생하진 않지만 동시 상황에서 무엇인가를 공유하기 시작하면 지옥 문을 여는 것과 동일한 효과를 가져온다. 단순히 하나만 공유해도 이런데 프로세스 내에서 모든 자원을 공유하는 스레드는 문제 발생의 정도가 더 심각하다. 이런 환경은 개발자에게 엄청나게 많은 문제들을 안겨주었고, 더불어 기존에는 고민할 필요도 없었던 문제들에 대해서 고려하도록 만들었다. 그리고 이 시점부터 더 이상 프로그램은 결정론적으로 동작하지 않게 되었다. 이는 달리 표현하면 재현 불가능한 무수한 버그들이 스레드라는 판도라의 상자를 통해서 세상으로 출현했다는 것이다.


이런 여러 이유로 멀티 스레드 프로그래밍은 윈도우 프로그래밍 중에서도 매우 어려운 부분에 속한다. 그럼에도 점점 더 PC의 코어 수는 늘어가고 멀티 스레드 프로그래밍은 점점 더 당연한 일이 되어가고 있다. 왜 멀티 스레드 프로그래밍이 어려운 것일까? 아마도 우리의 생각 자체가 병렬적으로 동시 처리하는 것에 익숙하지 않은 점에 그 근본적인 원인이 있을 것 같다. 이번 시간에는 컴퓨터에서 멀티태스킹을 구현하는 기본적인 구조와 멀티태스킹이 가져오는 동기화 문제에 대해서 살펴보도록 하자.


시분할

요즘은 CPU가 여러 개 달린 컴퓨터가 일반적이다. 스마트폰에도 듀얼 코어가 탑재되는 세상이니 PC는 두 말하면 잔소리다. 이렇게 CPU가 여러 개 달린 컴퓨터에서는 동시에 실행하는 것이 하나도 이상하지 않다. 장치 개수만큼 동시에 실행할 수 있는 것은 누가 봐도 당연한 일이기 때문이다. 하지만 Windows가 인기를 끌듯 말듯했던 Windows 3.1 시절부터 Windows XP가 등장하던 시점까지는 이러한 멀티 코어가 일반적인 PC 환경은 아니었다. 그 당시 컴퓨터에는 CPU가 하나라는 것이 거의 고정적이었던 시절이었다. 그렇다면 어떻게 이 CPU가 하나인 환경에서 여러 개의 프로그램을 동시에 실행할 수 있었던 것일까? 여기에는 약간의 눈속임이 있다.


눈속임을 쉽게 이해하기 위해서 우리가 공부를 하는 과정에 비유를 들어서 먼저 살펴보도록 하자. 텅 빈 책상이 있고, 책상 앞 의자에 우리가 앉아 있는 상황이다. 책상에는 국어, 영어, 수학 문제집이 놓여 있다. 이 문제집을 다 푸는 것이 오늘 우리의 할 일이다. 이 미션을 달성하는 방법에는 크게 두 가지 전략이 존재한다고 볼 수 있다. 하나는 국어 문제집을 다 풀고, 영어 문제집을 다 풀고, 수학 문제집을 다 푸는 형태로 순차적으로 하나씩 작업을 완료해 나가는 방식이고, 다른 하나는 국어, 영어, 수학 문제집을 번갈아 가면서 조금씩 풀어 나가는 방식이다. 멀티태스킹의 비밀은 바로 두 번째 방법에 있다. 문제집을 번갈아 가면서 푸는 상황에서 문제집을 교체하는 시간을 단축시키다 보면 어느 순간부터는 마치 우리가 세 개의 문제집을 동시에 풀고 있는 것처럼 느껴진다는 사실을 이용한 것이다.


운영체제는 여러 개의 프로그램을 동시에 실행하기 위해서 우리가 문제집을 풀었던 것과 동일한 방식으로 CPU에 프로그램 코드를 할당한다. 탐색기, 계산기, 그림판이 실행되어 있다면 Windows는 탐색기 코드를 조금 실행하고 계산기 코드를 또 조금 실행하고 이어서 그림판 코드를 실행하고는 다시 탐색기 코드를 실행하는 식으로 동시에 실행된 프로그램의 코드들을 잘게 나누어서 반복적으로 실행함으로써 마치 우리 눈에는 여러 개의 프로그램이 동시에 실행되는 것처럼 보이도록 만든다.


문제집을 번갈아 가면서 풀려면 문제집을 바꿀 때에 자신이 마지막으로 풀었던 문제가 어디인지를 기록해 두어야 한다. 그래야 나중에 다시 그 문제집을 풀 때에 어디서부터 풀어나가야 할지 기억할 수 있기 때문이다. 이렇게 마지막으로 풀었던 문제가 어떤 것이었는지를 기억하는 것과 마찬가지로 멀티태스킹에서도 이러한 것들이 필요하다. 작업을 이어가기 위해서는 중지했던 작업을 어디서부터 이어가야 할지를 알아야 하기 때문이다. 이렇게 작업을 이어가기 위해서 중단했던 시점의 기록을 “컨텍스트”, 작업들을 전환하는 것을 두고는 “컨텍스트 스위칭”이라고 표현한다.


문제집을 번갈아 가면서 푼다는 이 방법을 좀 더 살펴보면 재미있는 현상이 있다. 문제집을 너무 띄엄띄엄 번갈아 풀 경우에는 세 개를 동시에 푼다는 느낌이 없어지고, 문제집을 자주 번갈아 풀다 보면 특정 시점 이상부터는 문제집을 푸는 시간보다 문제집을 바꾸는 시간이 더 많아진다는 점이다. 이는 시분할을 사용한 멀티태스킹 시스템에서는 모두 발생하는 문제로 컨텍스트 교체 비용이 공짜가 아니라는 점을 보여준다. 따라서 운영체제는 멀티태스킹의 품질과 교체 비용을 고려해서 작업 교체 빈도를 적절하게 결정할 필요가 있다.


협력형 vs 선점형

앞선 국영수 문제집 풀이 예를 다시 생각해 보자. 여기서 우리가 문제집을 번갈아 푼다고 생각했을 때 이를 통제하는 방식 또한 크게 두 가지 형태로 나뉠 수 있다. 문제집을 푸는 당사자가 자유롭게 일정시간 한 문제집을 풀다가 다음 문제집을 푸는 형태로 번갈아 가면서 푸는 방식이 있을 수 있고, 다른 하나는 선생님이 일정시간마다 들어와서 다음 문제집을 풀도록 지시하는 방법이 있을 수 있다.


그렇다면 이 두 방식에는 각각 어떤 장단점들이 있을까? 우선 스스로 관리하는 방법을 생각해 보자. 이 방법의 장점은 효율성의 극대화에 있다. 문제를 푸는 당사자가 스스로 문제집을 교체하기 때문에 언제 교체하는 것이 가장 유리한지를 알 수 있다는 점이다. 다른 부가적인 장점으로는 선생님이라는 외부 존재가 필요하지 않다는 점을 들 수 있겠다. 이는 다시 말하면 비용이 절감된다는 말이다. 이에 반해서 두 번째 방법은 선생님이라는 추가 비용이 발생하지만 문제집 교체가 일정시간마다 반드시 발생하기 때문에 문제집 세 개에 할당되는 시간이 비슷해 진다. 멀티태스킹의 관점에서 보자면 일정 수준 이상으로 멀티태스킹 품질을 유지할 수 있다는 것을 의미한다.


이게 바로 협력형과 선점형 멀티태스킹의 차이다. 협력형 멀티태스킹이란 멀티태스킹 작업들이 협력해서 동시에 실행되는 구조를 말한다. 프로그램 실행 중간에 스스로 컨텍스트 전환을 해도 되는 시점이라고 판단이 된다면 다음 작업에게 CPU를 양보해주는 형태로 멀티태스킹이 진행된다. Windows 3.1이 이러한 방식을 채택했었다. 하지만 앞서도 언급했듯이 이 방식의 단점은 특정 작업이 지나치게 오래 CPU를 독점할 경우 전체 멀티태스킹의 품질이 떨어질 수 있다는 점에 있다. 더욱 치명적인 문제는 특정 작업의 버그로 인해서 CPU 자원을 다른 작업에게 양보하지 않을 경구 운영체제 자체가 다운될 수 있다는 점이다.


선점형 멀티태스킹이란 협력형과는 반대로 작업 제어 권한을 커널이 가지는 구조를 말한다. 이는 모든 작업이 임의의 시점에 커널에 의해서 선점될 수 있음을 의미한다. 선점된다는 말은 CPU 자원이 커널에게로 다시 넘어간다는 것이다. 여기에선 특정 작업이 CPU를 독점할 수도 없으며, CPU를 독점하려 한다고 해도 커널이 원하면 언제든지 CPU 제어권을 뺏어올 수 있다. 즉, 커널이 선생님의 역할을 한다고 할 수 있다. 이 방식의 장점은 앞에서도 설명한 것과 같이 안정적으로 멀티태스킹을 수행할 수 있으며, 작업들의 버그로 시스템다운이라는 치명적인 결과가 나타나진 않는다는 점이 있다. Windows 95 이후의 모든 Windows 운영체제는 이러한 선점형 멀티태스킹 방식으로 멀티태스킹을 구현하고 있다.


스레드

전통적으로 운영체제에서 작업이란 프로세스란 개념으로 표현된다. 따라서 운영체제는 앞서 설명한 것처럼 이런 프로세스를 동시에 여러 개 실행할 수 있도록 만들기 위해서 프로세스의 코드를 번갈아 가면서 수행한다. 이렇게 멀티태스킹 운영체제를 만들어놓고 보니 만들어진 프로세스들 중에는 다른 프로세스와 대규모의 자료를 공유해야 하는 것들이 있었다. 이런 프로세스들은 단순히 자료를 공유하기 위해서 운영체제의 IPC라는 복잡하고 비싼 메커니즘을 사용하고 있었다. 여기에서 이런 의문을 품은 사람들이 생겨난다. 왜 이렇게 멍청하게 하고 있지? 처음부터 모든 것을 자동으로 공유하도록 해준다면 어떨까? 이런 생각이 스레드라는 개념의 초기 발상이다. 많은 유닉스 운영체제는 이 개념의 생각대로 스레드를 특수한 형태의 프로세스로 처리한다. 실제로 리눅스의 경우에는 스레드라는 별도의 커널 객체를 두기 보다는 단순히 동일한 커널 구조체를 참조하는 특수한 프로세스로 구현했다.


하지만 Windows는 이러한 방식이 아닌 좀 다른 방식으로 스레드와 프로세스의 관계를 정리했다. 일단 기존의 스케줄링 단위를 프로세스에서 스레드로 바꾸어 버렸고, 프로세스는 스레드가 수행될 때 참조하는 컨테이너 역할로 만들어버렸다. 이렇게 만들어버리자 Windows에서는 스레드가 없는 프로세스란 아무런 의미가 없어졌다. 그래서 Windows에서 모든 스레드 종료란 프로세스의 종료를 의미한다.


다시 정리하면 이렇다. Windows 환경에서 프로세스란 실행 환경을 제공해주기 위한 컨테이너 역할을 한다. 여기에는 주소 공간과 접근 권한 같은 것들이 포함된다. 프로세스 자체로는 수행 가능한 객체가 아니며 내부에서 하나 이상의 스레드를 수행시킬 수 있는 환경에 불과하다. 스레드는 스케줄링의 기본 단위이며 하나의 실행 흐름을 나타낸다. 스레드는 수행되기 위해서는 해당 스레드가 수행될 프로세스 정보가 있어야 한다.


동기화

스레드가 이야기가 나오면 기본 안주로 함께 등장하는 메뉴가 동기화 문제다. 왜 그럴까? 그 답을 알기 위해서는 “동기화란 무엇인가?” 라는 질문에 대한 답을 먼저 찾아야 한다. 동기화란 바로 코드가 실행되는 순서를 정하는 일을 말한다. 어떤 코드를 먼저 실행할지 어떤 코드를 나중에 실행할지 순서를 정하는 일이다. “프로그램은 항상 프로그래머가 정해둔 순서대로 실행되는 것 아닌가요?”라는 의문을 가질 수 있다. 맞다. 프로그램은 항상 프로그래머가 정해둔 순서대로 실행된다. 하나만 실행될 때에는 말이다. 하지만 동시에 실행되기 시작하면 문제가 달라진다. 개별 실행 흐름들은 기존과 마찬가지로 순차적으로 수행되지만 그 흐름들이 섞이면 전혀 생각지도 못한 문제를 발생시킨다. 이를 방지하기 위해서 섞여서 실행되는 코드들 사이에 순서를 정하는 일이 필요해진 것이다. 그래서 동기화 문제란 항상 동시성과 같이 나올 수 밖에 없다.


동시성 vs 병렬성

비슷해 보이는 이 두 말은 큰 차이를 가지고 있다. 우선 동시성이란 우리에게 동시에 실행되는 것처럼 보이는 것들을 모두 나타내는 말이다. 따라서 단일 CPU 상에서 시분할 기법을 사용해서 여러 개의 프로그램을 수행하는 것도 동시성에는 포함된다. 반면 병렬성이란 복수의 CPU를 통해서 코드가 진짜 동시에 실행되는 상황을 나타낸다. 이런 의미에서 병렬성이란 물리적인 동시성을 나타낸다고 생각하면 되겠다.


<리스트 1>에는 동기화 문제를 보여주는 간단한 코드가 나와 있다. PinGame 함수는 핀을 서로 하나씩 가져가면서 마지막 핀을 가져가는 사람이 이기는 게임을 묘사한 코드다. 이제 이 함수가 스레드로 여러 개가 동시에 실행된다고 가정해보자. 그러면 개별 스레드는 이 하나의 코드를 순차적으로 실행하겠지만 운영체제 관점에서 보자면 이 코드들이 섞여서 동시에 실행되는 셈이 된다. 이렇게 동시에 실행되면 하나만 실행될 때에는 전혀 생각지도 못했던 문제가 발생하는데 이렇게 동시에 실행되면서 발생하는 문제를 동기화 문제라고 말한다.


우선 동기화 문제를 보다 효과적으로 설명하기 위해서 간단한 표현식을 정의하자. “(숫자)”는 숫자에 해당하는 스레드로 컨텍스트가 변경됐음을 나타낸다. a, b와 같은 기호는 코드 상의 주석에 나타나 있는 지점이 실행됐음을 의미한다. 따라서 “(1)ab”라는 것은 1번 스레드가 a, b라는 코드를 실행했음을 나타낸다. 마찬가지로 “(1)ab(2)a”라는 것은 1번 스레드가 a, b를 수행하고 2번 스레드로 컨텍스트 전환돼서 a라는 코드가 실행됐음을 나타낸다.


리스트 1 동기화 문제를 가진 코드


view plaincopy to clipboardprint?

int pin = 2;  

void PrintWinner()  

{  

    for(;;)  

    {                                 // ... a  

        if(pin == 1)                  // ... b  

        {  

            --pin;                    // ... c  

            printf("you win\n");      // ... d  

            break;  

        }  

        else  

            --pin;                    // ... e  

    }  

}  

자 그렇다면 앞서 정의한 표현식을 토대로 <리스트 1>의 코드에서 동기화 문제가 발생하는 흐름을 찾아보도록 하자. 바로 “(1)abea(2)ab(1)bcd(2)cd”가 그것이다. 문제는 결국 “you win”이라는 문자열이 화면에 두 번 출력된다는 것이다. 이런 코드들을 보여주고 여기까지 설명하면 대부분의 학생들은 <리스트 1>의 코드를 고침으로써 문제를 해결할 수 있다고 생각한다. 그리고는 거의 대부분 코드를 <리스트 2>와 같이 고친다.


리스트 2 동기화 문제를 가진 코드


view plaincopy to clipboardprint?

int pin = 2;  

void PrintWinner()  

{  

    for(;;)  

    {  

        if(pin <= 0)  

            break;  

  

        --pin;           // … a  

        if(pin == 0)     // … b  

        {  

            print("you win\n");  

            break;  

        }  

    }  

}  

그렇다면 과연 <리스트 2>의 코드는 문제가 없을까? 언뜻 보기에는 뭔가 좀 더 명확해 보여서 문제가 없어 보이지만 좀 깊이 생각해보면 <리스트 2>의 코드도 문제가 생기는 흐름을 찾아낼 수 있다. “(1)a(2)abc(1)bc”가 그것이다. 이 코드는 동기화 문제가 어디에서 비롯되는지 보다 명확하게 보여준다. 바로 값의 변경과 비교 사이에 발생하는 컨텍스트 전환이다. 이 말은 pin의 값 변경과 0과 비교하는 부분이 하나의 덩어리로 뭉쳐서 연산이 되지 않고서는 절대로 이 문제를 해결할 수 없다는 걸 의미한다.


여기까지 이야기하면 “if(--pin == 0)”과 같은 코드를 쓰면 된다고 생각하는 사람들이 있다. 하지만 “if(--pin == 0)”과 같은 표현식도 C언어 상으로는 한 줄로 표기되기 때문에 한번에 실행된다고 생각하기 쉽지만 컴파일러가 생성해낸 기계어는 <리스트 2>의 코드와 마찬가지로 여러 개의 기계어로 구성되어 있기 때문에 실행하는 사이에 컨텍스트 스위칭이 발생할 수 있고, 최종 프로그램에서는 동일한 문제가 발생한다.


이를 해결하기 위해서는 앞에서 우리가 내린 결론, 값의 변경과 비교가 한 덩어리가 되어서 실행되도록 만들어줄 어떤 도구가 필요하다. 이런 기능을 제공해주는 도구가 바로 운영체제에서 지원해주는 동기화 객체다. <리스트 3>에는 이러한 도구 중 하나인 크리티컬 섹션을 사용해서 문제를 해결한 코드가 나와있다.


리스트 3 크리티컬 섹션을 통한 동기화 문제 해결


view plaincopy to clipboardprint?

CRITICAL_SECTION cs;  

EnterCriticalSection(&cs);                   // … a  

--pin;                                       // … b  

if(pin == 0)                                 // … c  

{  

    LeaveCriticalSection(&cs);               // … d  

    print("you win\n");                      // … e  

    break;  

}  

LeaveCriticalSection(&cs);                   // … f  

크리티컬 섹션은 해당 객체를 진입한 상태에서 다른 스레드가 다시 진입하지 못하도록 만드는 기능을 한다. “(1)a(2)”는 가능하지만 “(1)a(2)a”는 불가능하다는 말이다. “(1)a(2)(1)b(2)(1)c(2)”와 같이 1번 스레드가 a를 수행하고 나면 1번 스레드가 d를 수행하기 전까지 다른 스레드로의 컨텍스트 스위칭은 발생할 수 있지만 해당 스레드가 a를 수행 시키는 것은 불가능하다. 결국 실행 흐름의 입장에서는 중간 중간 잡음이 섞이긴 하지만 결과론적으로 b와 c가 항상 한번에 수행되는 것과 동일한 효과를 나타낸다.


그렇다면 운영체제는 어떻게 이런 것들을 만들 수 있었을까? 운영체제가 만든 코드라고 하더라도 결국 CPU 상에서 동작하기는 우리가 만든 코드와 다를 수 없기 때문이다. EnterCriticalSection 내부에서도 언제든지 컨텍스트 전환이 발생할 수 있지 않을까? 당연한 이야기다. 이 문제를 해결하기 위해서는 크게 두 가지 종류의 해법이 있다. 하나는 운영체제 스케줄러를 특수하게 만들어서 EnterCriticalSection과 같은 함수의 수행 도중에는 절대로 컨텍스트 전환이 발생하지 않도록 만드는 것이다. 다른 하나는 보다 낮은 단계에서 값의 변경과 비교를 한번에 수행하는 연산을 사용하는 것이다. x86/64 계열의 CPU에는 이러한 목적을 위해서 cmpxchg라는 명령어를 제공한다. 비교와 변경을 한번에 수행하는 명령어다. Windows는 결국에는 이러한 명령어를 사용해서 동기화 객체를 구현한다. 이런 맥락에서 우리가 처음에 만든 프로그램도 cmpxchg라는 명령어를 사용하도록 만든다면 동기화 객체를 사용하지 않고도 문제를 해결할 수 있다.


지금까지의 논의는 단일 CPU 상에서 동시에 실행되는 코드들에 대한 것이었다. 멀티코어가 되면 문제가 좀 더 복잡해진다. 멀티코어란 CPU가 두 개 이상인 시스템을 말한다. 이런 시스템에서는 진짜 동시 실행이 가능해진다. 이 말은 지금까지 논의했던 방식으로는 멀티코어 환경에서 문제를 해결할 수 없다는 것을 의미한다. 앞서 동기화 문제를 해결하는데 일등 공신이었던 cmpxchg라는 명령어를 생각해보자. n개의 CPU가 있다고 가정한다면 n개의 CPU에서 동시에 cmpxchg가 실행될 수 있다. 결국 문제를 해결하기 위해서는 cmpxchg라는 명령어가 n개의 CPU가 아닌 한번에 하나의 CPU에서만 실행될 수 있도록 만들어주는 메커니즘이 필요하다. x86/64 계열의 CPU에서는 lock이라는 접두어가 이런 역할을 한다. lock cmpxchg를 하면 해당 시점에는 한 녀석만 메모리에 접근하도록 만들어준다. 재미있는 사실은 동시성에서 비롯된 동기화 문제를 해결하기 위해서는 결국 동시에 실행되지 않는 무엇인가가 필요하다는 점이다.


컨텍스트, 컨텍스트, 또 컨텍스트

스레드 이야기에는 필연적으로 다양한 종류의 컨텍스트라는 말이 등장한다. 의미는 다른데 이를 표현하는 말은 모두 컨텍스트이기 때문에 처음 접하는 개발자는 상당히 혼란스러울 수 있다. 여기에서 잠깐 이런 컨텍스트라는 말의 정체에 대해서 알아보고 넘어가도록 하자. 컨텍스트를 우리 말로 번역하면 문맥이라는 말이 된다. 문맥이란 무엇인가? 우리가 일상생활에서 접하게 되는 문맥이라는 말의 의미는 다음과 같은 간단한 대화 속에서 쉽게 파악할 수 있다.


영희: 너 밥 먹었니?

철수: 응. 먹었어.


일반적으로 구두 표현은 상황을 통해 유추할 수 있는 정보는 대부분 삭제된다. 우리는 철수의 대답에는 포함되지 않았지만 상황을 통해 ‘먹었어’라는 말을 철수가 밥을 먹었다라는 사실로 자연스럽게 확장시킬 수 있다. 이렇게 유추의 근거를 제공하는 말이 놓인 상황을 우리는 문맥이라고 부른다. 프로그래밍에서 말하는 것 또한 이와 별반 다르지 않다. 일반적인 프로그래밍 환경에서 말하는 컨텍스트라는 말은 해당 코드가 바인딩되어 실행되는 환경을 일컫는다. 물론 이렇게 이야기를 하더라도 바인딩되어 실행되는 환경이라는 것이 무엇인지 감이 잡히지 않는다. 이를 좀 더 구체적으로 살펴보기 위해서 <리스트 4>의 코드를 살펴보자.


리스트 4 콜백 함수를 사용한 프로그램


view plaincopy to clipboardprint?

std::list GlobalList;  

  

void Callback()  

{  

    GlobalList.push_back(GetTickCount());  

}  

  

int main()  

{  

    RegisterCallback(Callback);  

  

    for(;;)  

    {  

        if(!GlobalList.empty())  

       {  

           cout << GlobalList.front(); << endl;  

           GlobalList.pop_front();  

       }  

    }  

  

    return 0;  

}  

<리스트 4>의 코드에서 RegisterCallback 함수는 입력으로 들어간 콜백 함수가 랜덤 주기로 호출되도록 등록하는 함수다. 만약 RegisterCallback 함수 설명에 ‘입력으로 들어온 콜백 함수는 등록한 스레드 컨텍스트에서 호출된다’라는 말이 있다면 위 코드는 아무런 문제 없이 동작한다. 하지만 이와는 다르게 ‘입력으로 들어온 콜백 함수는 시스템 스레드 컨텍스트에서 호출된다’는 말이 있다면 위 코드는 잘못 동작한다. 이유는 Callback이 호출되는 스레드와 main이 실행되는 스레드가 다르기 때문이다. 서로 다른 스레드가 GlobalList를 동시에 접근함에도 동기화 처리가 되어 있지 않기 때문에 최악의 경우에는 프로그램 크래시가 발생한다. 이와 같이 Callback이라는 코드가 바인딩되어 실행되는 스레드를 우리는 컨텍스트라는 말로 표현한다.


여기서 더불어 살펴보아야 할 표현이 하나 있다. 바로 “임의의 스레드 컨텍스트”라는 표현이다. 대부분의 콜백 함수 설명에는 등록한 콜백 함수는 임의의 스레드 컨텍스트에서 호출된다라는 말이 있기 때문이다. 여기서 “임의의”라는 말은 정해지지 않았음을 나타내는 말이다. 그러니 “임의의 스레드 컨텍스트”라는 말은 달리 표현하면 “정해지지 않은 스레드 컨텍스트”가 된다. 그렇다면 도대체 정해지지 않은 스레드 컨텍스트라는 표현이 개발자에게 해주고 싶은 속내는 무엇이었을까? “정해지지 않았음”이 프로그래머에게 해주고 싶은 진짜 이야기는 이 말이다. “네 멋대로 특정 스레드 컨텍스트에서 호출된다고 생각하지 말아라. 이 코드는 네가 생각지도 못한 스레드 컨텍스트에서도 호출될 수 있다.”


“해당 콜백 함수는 임의의 스레드 컨텍스트에서 호출된다”는 문장을 보는 순간 여러분이 베스트 개발자라면 다음과 같은 생각을 반드시 해야 한다. “아, 이 함수가 실행되는 스레드는 정해지지 않았구나. 콜백 함수가 전역 데이터에 접근하는 경우에는 반드시 동기화 처리를 해줘야 하겠구나.” 반대로 “해당 콜백 함수는 콜백을 등록한 스레드와 같은 컨텍스트에서 호출된다”는 문장을 보게 되면 역으로 콜백과 등록한 스레드에서만 사용하는 공유 데이터가 있다면 동기화 처리를 생략해서 최적화 작업을 할 수 있겠다라는 생각을 하는 것이 정석이다.


지금까지 특정 함수가 수행되는 컨텍스트에 대해서 알아보았다. 이와 마찬가지로 특정 스레드도 해당 스레드가 수행되는 컨텍스트가 존재한다. 바로 프로세스다. 앞서 Windows에서 스레드의 개념을 설명할 때 특정 프로세스 내에서 수행되는 실행 흐름이라고 표현했다. 따라서 스레드가 동작하기 위해서는 반드시 어떤 프로세스 컨텍스트에서 동작할지를 알아야 한다는 말이다. 일반적으로 프로그램에서 생성하는 모든 스레드는 그 코드를 수행하는 스레드가 참조하는 프로세스 컨텍스트로 생성된다. 하지만 커널 모드 코드들의 경우에는 이 참조하는 프로세스 컨텍스트를 임의의 변경할 수 있다. 따라서 코드가 호출되는 스레드 컨텍스트와 마찬가지로 스레드가 수행되는 프로세스 컨텍스트도 변경될 수 있음을 알아야겠다.






-브라우저에서 입력한 정보는 request 객체에 담겨 서블릿으로 들어옴

-html형식으로 응답해줌

-서브릿은 html로 응답하기 위해 res객체의 write 객체에 넣어 보냄

-html 적은 부분을 nodejs는 render함수로 쉽게보냄





























































출처 : http://wanzargen.tistory.com/23?category=705682


이전에 자바 웹 프로젝트를 생성하는 것을 설명한 적이 있었당.


요것을 모르겠다면 이전 포스트 읽고오시길.


=>  2017/06/01 - [Programming/Lib, Tools] - Eclipse :: 이클립스에서 웹 애플리케이션(Web Application) 프로젝트 만들기



이번 포스트에서는 이클립스의 자바 웹 애플리케이션 구조 와  실무의 자바 웹 애플리케이션 구조  를 설명하고 어떻게 다른지 비교하면서


왜. 대체 왜 실무에서는 다른 프로젝트 폴더 구조를 사용하는지를 이야기하며 마무리하도록 하게따.



1. 이클립스 웹 애플리케이션 디렉토리 구조

$workspace/프로젝트명/build

/classes

/src

/WebContent

/META-INF

/WEB-INF

/lib


이거슨 이클립스의 자바 웹 애플리케이션 디렉토리 구조이다.

이전에 이클립스에서 웹 프로젝트를 만드는 방법을 소개했었는데, 그 2 가지 방법이 있었다. 
(이클립스의 다이나믹 웹 프로젝트를 만드는 방법과 gradle을 이용하는 방법.)

그 중, 손쉽게 만들 수 있는 이클립스 다이나믹 웹 프로젝트를 생성하게되면, 위와 같은 구조를 띈다.

그런데, 회사마다 다르겠지만. 이 구조는 잘 사용하지 않는다.


그렇담?? 뭐가 실무에서 많이 쓰이는데??

2. 실무의 웹 애플리케이션 디렉토리 구조 (=Maven 웹 프로젝트 구조)


실무에서는 대부분  메이븐의 웹 프로젝트 구조를 많이 쓴다.

개발을 돕는 툴들이 겁나게 많은데, 메이븐은 그 중 하나로, 사이트에 가보면 스스로를 이렇게 설명하고 있다.

Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.


비슷한게 Gradle이 요새는 뜨고 있는데 이와 관련된 내용은 다음에 제대로 포스팅 하는 것으로 하고. 

다시 본론으로 돌아와, 이게 그 폴더 구조이다.

$workspace/프로젝트명

/src

/main

/java

/resources

/webapp(=WebContent)

/WEB-INF

/test

/java

/resources

/bin



 

<디렉토리 설명>


-  src : 프로그램 소스 파일을 두는 곳


- main/java : 자바 소스 파일(.java파일)을 두는 곳


- main/resources : 프로그램을 실행할 때 사용하는 설정 파일(.properties, .xml 등)을 두는 곳


- main/webapp : HTML, CSS, JavaScript, GIF 등 정적 웹 자원을 두는 곳.

                   (JSP파일도 여기에 둠)


- main/webapp/WEB-INF : 웹 애플리케이션 정보 파일을 두는 곳


- test : 코드를 테스트하는 소스 파일을 두는 곳


- test/java : 단위 테스트 관련 자바 소스 파일을 두는 곳


-bin : 소스코드가 컴파일된 *.class 파일, *properties파일, *xml파일 등



그런데 우리의 이클립스는 메이븐 폴더구조를 알아서 딱딱 만들어주지 않는다.


이클립스로 웹 플젝트를 암만 만들어봐야 지네 플젝 구조를 만들어줄 뿐. 몹쓸.



3. 현업에서 메이븐 폴더 구조를 자바 웹프로젝트 디렉토리 구조로 많이 사용하는 이유

사람마다, 회사마다 모두 다른 IDE를 사용한다.

어떤 사람은 이클립스를 쓰는데 또 어떤 사람은  인텔리제이를 쓰고.. 

그럼 프로젝트를 함께 개발한다고 할 때, 공유하기가 매우 불편하다.


즉, 특정 IDE폴더구조를 사용하게 되면 공유하기가 불편하다. 


저사람과 내가 사용하는 폴더구조가 다르면, 어떤 파일은 여기에, 또 어떤 파일은 저기에 내가 직접 한땀한땀 복붙해줘야 하며

뭐 하나 잘못하면 잘 돌아가야 할 프로젝트가 안 돌아가는 대참사가 일어나게 마련.


이 때문에 전 세계 공통으로 사용하는 폴더구조가 있는데 그게  Maven 폴더 구조다.

이게 국제적으로 모든 개발자가 가장 많이 사용하는 폴더 구조인거다.


그리고 다른 사람과 공유할 일이 생기면, src폴더만 공유하면 된다.



그럼 이클립스에서 이 메이븐 폴더 구조를 사용하려면??


이클립스에서 메이븐 폴더 구조를 만들어주려면 src 폴더와 그 밑의 폴더들을 모두 직접 만들어주면 된다.


참고로 이클립스는 컴파일이 되면서 자동으로 bin 폴더를 생성한 후 그 곳에 class파일을 두니까 굳이 bin폴더를 만들어줄 필요는 없다.


src 폴더 구조만 만들어준 후, 이전 포스트에서 소개한 방법을 응용해주면 되시겠다.



끄읏-.

출처:http://wanzargen.tistory.com/21?category=700064




1. 톰캣 서버가 설치된 폴더를 등록하기

먼저 톰캣을 이클립스에서 실행하려고 하면, 톰캣이 내 컴퓨터 어디에 저장되어있는지 이클립스가 알야아 한다.


그래서 제일 먼저 해야 할 일은, 설치된 폴더를 찾아서 등록해주는 것이다.


그럼 해볼가.




Eclipse의 설정에서 Server > Runtime Environments 에 들어가보자. 

그러면 아래와 같은 화면이 뜰거시다.

여기서 Add 버튼을 눌러보자.




그럼 아래와 같은 창이 또 뜨는데, 저기에서 내 컴퓨터에 있는 톰캣 버전과 일치하는 항목을 선택한 후, Next 버튼을 클릭!





그러면 아래 화면이 되는데, 

Browse 버튼을 눌러서 실제로 내 컴터에 톰캣이 설치되어 있는,

지금 생각하는 바로 그 폴더를 찾아서 등록해준다.





이렇게 다 하고 Finish버튼을 눌러 완료해주면 끄읏. 짝짝짝-.



여기까지 이클립스에서 톰캣 서버를 실행할 수 있도록, 톰캣이 있는 폴더가 등록 완료된거다.



자. 그러면 이제 다된거 아님??

노노. 등록된거 어떻게 실행시킬거임?

모르겠지?


등록한 서버를 이클립스에서 편하게 실행시킬 수 있는 환경을 만들어보자.



2. 웹 애플리케이션 서버(톰캣) 실행환경 구축하기

 

이클립스에서 Windows > Show View > Servers 를 눌러주면,





요런 창이 뜬다. 이걸 앞으로 "Servers 뷰" 라고 하겠음.




저기에 파란색으로 써있는게 무슨말이냐면

"사용 가능한 서버가 없으니 여기를 눌러서 새로운 서버를 생성해다오"

이 말이다.


저기를 눌러주면,  아래와 같은 창이 뜬다.



어디서 많이 본듯한 이 창은 앞에서 톰캣 폴더를 등록해주는 바로 그 창과 비슷하다.

(앞의 그 창은, 폴더 등록해주는거고요, 이 창은 Servers 뷰에서 간편하게 실행시키기 위해 등록하는 창이다.)


당신의 그 톰캣 버전을 선택하고 Finish를 눌러 완료해주면...


아래처럼 Servers 뷰에 새로운 서버가 보일 것임.




오호라-.


그럼 어디 한번 실행시켜볼가.



3. 웹 애플리케이션 서버 실행환경 시작하기


저기다가 대고 오른쪽 마우스를 클릭하면 아래와 같이 context menu창이 뜬다.


거기서 Start 를 누르면-




이렇게 콘솔 뷰에 뭐라뭐라 뜬다.


톰캣이 정상적으로 시작이 되었나보다 헷.




막 빨간색이라서 첨에 에러난줄 알았ㄷ..






여기서 잠깐.


이클립스에서 톰캣을 실행시키면... 뭐 좀 편하긴 하겠는데,


단지 톰캣을 이클립스에서 편하게 시작/종료시키기 위해 이클립스에서 쓰는걸까?


답은 "노노".


서버가 사용자의 다양한 요청에 대해 동작하려면, 사용자의 요청에 해당하는 웹 애플리케이션이 있어야 한다.

그리고 톰캣은 개발자가 만든 바로 그 애플리케이션들을 실행할 수 있는 환경을 제공하는 서블릿 컨테이너의 역할을 한다.


따라서 웹 애플리케이션 파일을 톰캣 폴더 밑 어딘가에 저장해둔다.

(정확히 어디쯤 저장되는지는 다음 포스트에..)


그런데 문제는.. 수정이 일어나면 그 파일을 수정된 파일로 바꿔줘야 한다. 그래야 수정된 파일이 실행될 것이 아닌가.


뭐 하나 바꿀라 치면 톰캣 폴더 밑에, webapps폴더 밑에, WEB-INF폴더 밑에, classes 폴더 밑에 있는 기존의 파일을 지우고 새로운 파일로 교체....하.

생각만해도 욕지기가 나와.


그런데 이클립스에서 이러케 이러케 해주면, 안그래도 된다 이말씀-.이래서 이클립스 이클립스 하나봐.




정리.


이클립스에서 톰캣 웹 애플리케이션 서버 실행환경을 구축하여 사용하는 이유


=> 수정사항이 있을 때마다 파일을 교체해야 하는 번거로운 작업을 생략할 수 있으니까.












4. 실행환경이 구축된 폴더 확인하기


그러면 이클립스에서는 대체 뭘 어떻게 하길래, 수정된 파일을 바꿔주지 않아도 되는걸까?? 알쏭달쏭 궁금궁금


이클립스 workspace 폴더 > .metadata 폴더 > .plugins 폴더 > org.eclipse.wst.server.core 폴더에 한번 가보자.



<참고>


맥 사용자는  .metadata, .plugins폴더가 안보일 수 있다. 

hidden file이라서 그럼.


Command + Shift + .  이렇게 누르면 나오니까 당황말자.




저기 들어가보면 아래와 같은 파일들이 있을텐데 저기 있는 것들 중에 주목할 것은 "tmp0" 폴더이다.



tmp0 폴더는 이클립스에서 톰캣을 실행하기 전까지는 이 폴더가 안만들어진다.


마냑에 톰캣 실행환경을 여러개 만들어서 실행시키면 tmp0, tmp1, tmp2, ... 이런식으로 생긴다.

(tmp 뒤에 붙는 저 숫자는 폴더가 만들어진 순서대로 붙여지는거니까 신경 안써도 됨.)


저 폴더를 열어보면..


이렇게 많은 것들이 있다. 


이 폴더의 이름이 "tmp~"인데는 다 이유가 있다.


이 폴더는 이름 그대로 '임시폴더'다.


이클립스의 Servers 뷰에서 해당 실행환경을 삭제하면, 이 폴더는 사라진다.


즉, 개발자가 뭘 바꾸면, 똑똑한 이클립스는 지가 만든 임시폴더로 가서 개발자가 바꾼 그 파일을 복붙해놓는다.


그러니까 이 폴더에서 뭔가 열심히 바꿔도 소용이 없다. 서버 삭제하면 이 폴더도 다 날아가니까.



<참고2>


저기서 "wtpwebapps"폴더 밑에 "web01"이라는 폴더가 있는데, 이거슨 다음 포스트에서 소개할 웹 애플리케이션 프로젝트이다.


이클립스에서 웹 애플리케이션 프로젝트를 만들면 무조건 여기에 보이는게 아니고,

"이 프로젝트는 이 톰캣 실행환경에서 실행할거야!" 하고 등록하는 과정을 밟아줘야 함. (이거 다음 포스트에 있음. 겁나친절함.)






서버 구성 파일을 바꾸고 싶다면, 그것도 이클립스에서 바꿀 수 있다.


이클립스 Project Explorer에 가보면 Servers 폴더가 이미 있었거나 새로 생겼을텐데, 여기서 뭐 바꾸고싶은걸 바꾸면 알아서 저기 tmp 폴더 밑에 자동으로 반영이 된다.






-----------------


이것으로 이클립스에서 톰캣을 실행시키는 방법 & 왜 꼭 그래야 하는 이유가 잘 이해되었기를-.




+ Recent posts