쓰레드의 동기화 


  • 여러 쓰레드가 동시에 수행되는 멀티쓰레드는 하나가 다른하나에 영향을 미칠 수 있다.
  • 멀티쓰레드 프로세스의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업하기 때문에 서로의 작업에 영향을 주게 된다.

 

이러한 일이 발생하는 것을 방지하기 위해서 한 쓰레드가 특정 작업을 끝마치기 전까지 
다른 쓰레드에 의해 방해받지 않도록 하는 것이 필요하다. 그래서 도입된 개념이 바로 ‘임 
계 영역(critical section)’과 ‘잠금(락,lock)’이다

공유 데이터를 사용하는 코드 영역을 임계 영역으로 지정해놓고, 공유 데이터(객체)가 
가지고 있는 lock을 획득한 단 하나의 쓰레드만 이 영역 내의 코드를 수행할 수 있게 한 
다. 그리고 해당 쓰레드가 임계 영역 내의 모든 코드를 수행하고 벗어나서 lock을 반납해 
야만 다른 쓰레드가 반납된 lock을 획득하여 임계 영역의 코드를 수행할 수 있게 된다

 

 

  • 이처럼 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것을 ‘쓰레드의 동기화(synchronization)’라고 한다.
  • 동기화하려면 간섭받지 않을 문장들을 하나의 영역, 즉 임계 영역으로 묶으면 된다.
  • 이 묶는 행위는 synchronized 라는 키워드로 할 수 있다.
  • 임계영역은 락이 걸려 하나의 영역에 하나의 쓰레드만 출입 가능하게 된다. 

 

 

 

synchronized 를 이용한 동기화 


  • synchronized 로 임계영역을 설정하는 방법 두가지는 
  • 1. 메서드 전체를 임계영역으로 지정 (voide 앞에 synchronized 키워드 넣기)
  • 2. 특정 영역을 임계영역으로 지정하는 방법이 있다. synchronized(참조변수)
  • 첫 번째 방법은 메서드 앞에 synchronized를 붙이는 것인데, synchronized를 붙이면 메서드 전체가 임계 영역으로 설정된다
  • 두 번째 방법은 메서드 내의 코드 일부를 블럭{} 으로 감싸고 블럭 앞에 ‘synchronized (참조변수)’를 붙이는 것인데, 이때 참조변수는 탁을 걸고자하는 객체를 참조하는 것이어 야 한다.
  • 블럭을 synchronized 블럭이라고 부르며, 이 블럭의 영역 안으로 들어가면서 부터 쓰레드는 지정된 객체의 lock을 얻게 되고, 이 블럭을 벗어나면 lock을 반납한다.
  • 임계 영역은 멀티쓰레드 프로그램의 성능을 좌우하기 때문에 가능하면 메서드 전체에 락을 거는 것보다 synchronized블럭으로 임계 영역을 최소화해서 보다 효율적인 프로그 램이 되도록 노력해야한다.
  • 즉, 임계영역은 한번에 한 쓰레드만 들어갈 수 있는 만큼 그 영역 최소화 될 수록 좋다.

 

두 방법 모두 lock의 획득과 반납이 모두 자동적으로 이루어지므로 우리가 해야 할 일은 그저 임계 영역만 설정해주는 것뿐이다.

 

 

  • synchronized 하지않았을때는 한 쓰레드에 다른 쓰레드가 영향을 미쳐 마이너스 잔고가 뜨게 된다. 
  • 그 이유는 한 쓰레드가 if문의 조건식을 통과하고 출금하기 바로 직전에 다른 쓰레드가 끼어들 어서 출금을 먼저 했기 때문이다.
  • 즉, synchronized 키워드가 없을 때 이런 결과가 출력될 수 있다. 
  • withdraw메서드에 synchronized키워드를 붙이기만 하면 간단히 동기화가 된다.

 

 

 

  • 그러나 synchronized  키워드를 적어 해당 메소드를 임계영역으로 만들어주면 
  • 잔고가 마이너스로 출력되는 결과가 해결된다. 

 

 

  • 예제에서도 synchronized 키워드가 없으면 한쓰레드가 다른 쓰레드에 영향을 미쳐 잔고가 마이너스로 출력된다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

join()


  • join은 작업하지않고 지정된 시간동안 다른 쓰레드가 작업하는 것을 기다리는 메소드다. 
  • 쓰레드 자신이 하던 작업을 잠시 멈추고 다른 쓰레드가 지정된 시간동안 작업을 수행하도록할 때 join()을 사용한다.
  • join()은 시간이 얼마나 됐든 작업이 끝날때까지 기다리며
  • join(long millis)는 정해진 시간동안 기다린다. 
  • join()도 sleep()처럼 interrupt()에 의해 대기상태에서 벗어날 수 있으며,
  • join()이 호출 되는 부분을 try-catch문으로 감싸야 한다.
  • join( )은 여러모로 sleep( )과 유사한 점이 많은데,
  • sleep()과 다른 점은 join()은 현재 쓰레드가 아닌 특정 쓰레드에 대해 동작하므로 static메서드가 아니라는 것이다

 

 

  • 참고로 콘솔창의 결과가 한줄로 나온다면 word wrap 버튼을 눌러 한화면안에 출력되도록 할 수 있다. 

 

 

 

  • join 메소드가 th1, th2 가 실행될동안 기다리는 작업을 했기에 
  • 콘솔창 마지막 소요시간 출력을 통해 
  • 해당 main 메소드는 가장 마지막에 실행을 마무리짓고 종료된 것을 확인할 수 있다. 

 

 

  • join() 메소드가 주석처리되면 main 메소드가 소요시간을 가장먼저 출력 후 종료되고
  • th1 과 th2가 차례로 실행된다. 
  • 소요시간은, join이 실행되기전 현재시간을 startTime 으로 받고 
  • 쓰레드가 종료되는 시점의 시간을 받아 앞선 startTime을 빼는 것이다. 
  • 종료시간 - 시작시간 = 소요시간

 

 

 

join() - 예시


  • 먼저 sleep()을 이용해서 10초마다 한 번씩 가비지 컬렉션을 수행하는 쓰레드를 만든 다음,
  • 쓰레드를 생성해서 데몬 쓰레드로 설정하였다
  • 데몬쓰레드는 반복문 while 로 작성되어있으며 값을 true 로 지정해 무한루프를 돌게한다. 
  • 무한루프를 도는 동안 10초를 기다리고, 만약 interrupt 등의 방해를 받았다면 예외를 출력한다. 
  • 일반쓰레드가 없으면 데몬쓰레드는 자동종료되므로 무한루프를 탈출하여 gc()를 수행한다.  
  • 가비지 컬렉터는 사용하지않고있는 객체를 제거해주는 역할을 한다. 

 

 

  • 만약 예외가 발생하는 경우, 무한루프를 벗어나 gc()를 수행한다. 

 

 

  • 쓰레드 gc를 깨우는 것뿐만 아니라 join()을 이용해서 쓰레드 gc 가 작업할 시간을 어느 정도 주고
  • main쓰레드가 기다리도록 해서, 사용할 수 있는 메모리 가 확보된 다음에 작업을 계속하는 것이 필요하다.

 

 

 

  • 이때 for문 내에 try~catch 문안에서 join 메서드가 필요하다. 

 

 

 

 

yield()


  • yield()는 쓰레드 자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보(yield)한다.
  • 남는 시간을 다음 쓰레드에게 양보하고, 현재 쓰레드는 실행 대기 상태가 되는 메서드이다.  
  • static 메서드이므로 다른 쓰레드에게 양보하도록 하지못하며 자기 자신(현재쓰레드) 에게만 적용된다. 
  • yield 와 interrupt를 적절히 사용하면 응답성과 효율성을 높일 수 있다. 

 

  • 만약 필드값을 suspended값을 true 로 둔다고 가정해보자.
  • 즉 잠시 실행을 멈추게 한 상태라면, 쓰 레드는 주어진 실행시간을 그저 while문을 의미없이 돌면서 낭비하게 될 것이다. 이런 상황을 ‘바쁜 대기상태(busy-waiting)’이라고 한다.
  • 이때는 while 내부에서 무한 루프를 돌되 if문안에선 작업이 수행되지않을것이다. 
  • 이때 하는 일 없이 무한루프만 돌게되는데 이런 현상을 busy-waiting 이라고 한다. 

 

 

  • 해결법은 예외에 현재쓰레드에대한 yield를 호출하는 것이다. 
  • 그러면 남는 시간동안 무한루프에 갇히지 않고 남는 시간을 양보할 수 있게 된다.
  • 즉, 같은 경우에 yield()를 호출해서 남은 실행시간을 while문에서 낭비하지 않고 다른 쓰레드에게 양보(yield)하게 되므로 더 효율적이다.
  • 다만 yield 는 OS 스케줄러에게 통보하는 것이지 반드시 원하는 만큼 시간을 효율적으로 관리 할 수 있게 됨을 의미하지 않음을 알아야 한다.   

 

 

 

 

 

 

 

 

 

 

 

suspend(), resume(), stop()


  • 쓰레드의 실행을 각각 일시정지, 재개, 완전정지 시킨다.
  • suspend()는 sleep()처럼 쓰레드를 멈추게 한다.
  • suspend()에 의해 정지된 쓰레드는 resume()을 호출해야 다시 실행대기 상태가 된다.
  • stop()은 호출되는 즉시 쓰레드가 종 료된다.
  • 단 교착상태 (deadlock)를 일으키기 쉽게 작성되어있으므로 deprecated 되었기에 사용이 권장되지않는다.
  • ‘deprecated’의 의미는 ‘전에는 사용되었지만, 앞으로 사용하지 않을 것을 권장한다.’이다.
  • ‘deprecated’된 메서드는 하위 호환성을 위해서 삭제하지 않는 것일 뿐이므로 사용해서는 안 된다

 

 

  • 다음은 각각의 메서드를 테스트 하는 예제이다.
  • 별을 하나찍는 th1의 suspend()를 호출시 두개, 세개의 별만 찍히는 것을 확인할 수 있다.
  • sleep(2000)은 쓰레드 를 2초가 멈추게 하지만, 2초 후에 바로 실행상태가 아닌 실행대기상태가 된다.

 

 

  • 별을 하나찍는 th2의 suspend()를 호출시 세개의 별만 찍히는 것을 확인할 수 있다.

 

 

  • 별을 하나찍는 th1의  resume()를 호출시켜 재개시, 하나의 별이 다시 찍히기 시작함을 확인할 수 있다.

 

  • 현재 쓰레드의 2초의 sleep을 건뒤 별을 세개 찍는 th3의 stop 메서드 호출시 
  • sleep이 걸리기 전 2초간 th3의 세개의 별이두번 찍힘을 확인할 수 있다. 

 

 

  • 해당 메서드 들이 deprecated 된 상태기 때문에 쓰레드를 조금 변경시켜 MyThread 라는 쓰레드 생성 후 테스트 한다.
  • 단, 여기서 쓰레드의 동작이 모두 끝났어도 서버가 종료되지않는 현상을 볼 수 있는데
  • 이는 쓰레드의 필드 앞에 volatile 을 붙여주면 해결된다. 

 

 

  • volatile 키워드가 붙으면 해당 메서드는 가변적인 상태라는 뜻이다
  • 어떠한 필드, 변수를 생성시 각각 ram에 원본이, cpu에 복사본이 할당되는데 
  • volatile키워드가 붙음은 이 복사본을 사용하지않고 원본만을 참고하겠다는 의미이다.
  • 그럼으로서 값이 true, false로 변경될때마다 원본-복사본이 아니라 원본만을 참고하여 빠른 반영이 가능해진다. 

 

 

 

 

 

 

 

sleep()


  • sleep() 은 쓰레드의 메서드로서 현재 쓰레드를 지정된 시간동안 멈추게 한다.
  • '현재' 쓰레드인 이유는 해당 메서드가 static 이기 때문이다. 
  • 즉 다른, 특정 쓰레드를 멈추게 하는 것은 불가능하며 자기자신만 가능하다. 
  • 그러므로 변수명.sleep() 으로 작성하는게 아니라 클래스명인 Tread.sleep() 으로 작성한다. 
  • 반드시 try~catch 문으로 예외처리를 해주어야 한다. 
  • long mills는 천분의 일초 단위로서 3초를 의도하고싶다면 3000 으로 작성해야 한다. 

 

 

  • 예제는 쓰레드가 총 세개인 상태다. main, th1, th2

 

 

  • 쓰레드 th1과 th2에 대해 start()를 호출하자마자 ‘th1.sleep(2000)’을 호출하여 쓰레드 th1이 2초 동안 작업을 멈추고 일시정지 상태에 있도록 하였으니까 쓰레드 th1이 가장 늦 게 종료되어야 하는데 결과에서는 제일 먼저 종료되었다.
  • 그 이유는 sleep()이 항상 현재 실행 중인 쓰레드에 대해 작동하기 때문이다. static 이기 때문이다.
  • ‘th1.sleep(2000)’과 같이 호출하였어도 실제로 영향을 받는 것은 main메서드를 실행하는 main 쓰레드이다.
  • 그래서 sleep()은 static으로 선언되어 있으며 참조변수를 이용해서 호출하기 보다는 ‘Thread.sleep(2000);’과 같이 해야 한다.

 

 

  • try~catch... 문을 작성해 sleep을 걸어주지 않으면 현재 메서드(<<main종료>>)가 가장먼저 끝나버리는걸 확인할 수 있다.
  • 즉, th1, th2,main 쓰레드가 차례로 종료되어야 맞는데 main이 가장먼저 끝나버린 것이다.

 

 

  • 예외처리를 해주면 다시 main 쓰레드가 가장 마지막에 종료된 것을 확인할 수 있다.

 

 

  • 반드시 예외처리를 해주어야 하는 이유는 sleep 메서드가 InterruptedException으로 예외처리를 하며
  • InterruptedException가 Exception 클래스를 상속하고 있기 때문이다. 

 

 

  • try~catch 처리를 해주지않으면 에러가 발생한다.

 

 

 

 

 

interrupt()


  • 대기상태인 쓰레드를 실행대기 상태로 변경하는 메서드이다. 
  • 여기서 대기상태작업중단을 의미하며 sleep(), join(), wait() 등이 있다. 
  • interrupt() : 쓰레드의 interrupted 상태를 false -> true 로 변경
  • isInterrupted() : 쓰레드의 interrupted 상태 반환
  • interrupted() : 현재 쓰레드의 interrupted 상태를 반환하며, false로 초기화, static
  • 단지 멈추라고 요청 만 하는 것일 뿐 쓰레드를 강제로 종료시키지는 못한다.
  • interrupt()는 그저 쓰레드의 interrupted상태(인스턴스 변수)를 바꾸는 것일 뿐이다 (true > false)
  • interrupted()는 쓰레드에 대해 interrupt()가 호출되었는지 알려준다.
  • interrupt() 가 호출되지 않았다면 false를,interrupt() 가 호출되었다면 true를 반환한다
  • 예를 들면, 브라우저에서 어떤 파일을 다운로드 받다가 로딩이 너무 길어질시 취소버튼을 누르는 예시를 생각해볼 수 있다.
  • 우리가 취소를 누르면, 쓰레드는 이를 interrupt 상태로 받아 들이고, 메서드 내에서 다운로드를 위한 반복문을 빠져나와 쓰레드가 종료되는 것이다. 

  • interrupt() 가 호출되면,interrupted( )의 결과가 false에서 true로 바뀌어 while문을 벗 어나게 된다

 

 

  • 다음 예시는, ThreadEx9_1 이라는 카운트를 세도록하는 별개의 쓰레드를 만든후 
  • 1) 실행시 쓰레드를 start하여 2) 카운트가 도는 중 3) I/O 입출력이 발생하면 4) 쓰레드를 멈추도록 하는 로직이다.
  • 이때 interrupt를 호출하면 interrupted 상태가 true가 된다. 일종의 방해/중단을 받았기 때문이다.

 

 

  • 이때 interrupted를 호출하면 첫번째출력값은 true지만 두번째 출력값은 false 이다.
  • 첫번째는 방해받았음을 나타내지만 두번째는 그사이 해당메서드가 true->false 설정하는 작업을 했기때문이다.
  • 이때 interrupted 객체는 th1.이 아니라 Tread로 작성함에 유의한다.
  • interrupted 는 static 메서드로서 현재 쓰레드만을 설정할 수있다. 즉, 다른 쓰레드에 영향을 미치지 못한다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

데몬 쓰레드(Demon thread)


  • 쓰레드의 종류에는 일반쓰레드와 데몬쓰레드가 있는데 데몬쓰레드는 보조적인 역할을 하는 쓰레드를 말한다.
  • 데몬 쓰레드는 다른 일반 쓰레드(데몬 쓰레드가 아닌 쓰레드)의 작업을 돕는 보조적인 역할을 수행하는 쓰레드이다.
  • 보조적인 역할이다보니 일반쓰레드가 종료되면 자동으로 종료된다.
  • 그 이유는 데몬 쓰레드는 일반 쓰레드의 보조역할을 수행하므로 일반 쓰레드가 모두 종료되고 나면 데몬 쓰레드의 존재의 의미가 없기 때문이다
  • 데몬 쓰레드의 예로는 가비지 컬렉터, 자동저장, 화면 자동갱신등이 다.
  • while 이나 for문등의 무한루프를 이용해서 만든다. 즉, 무한루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 만족 되면 작업을 수행하고 다시 대기하도록 작성한다
  • 무한루프를 사용하지만 보조적인 쓰레드이다보니 일반쓰레드가 종료되면 무한히 돌지않고 함께종료된다.

  • 데몬 쓰레드는 일반 쓰레드의 작성방법과 실행방법이 같다.
  • 다만 쓰레드를 생성한 다음, 실행하기 전에 setDaemon(true)호줄하기만 하면 된다.
  • 그리고 데몬 쓰레드가 생성 한 쓰레드는 자동적으로 데몬 쓰레드가 된다는 점도 알아두자

 

 

 

  • isDaemon() : 쓰레드가 데몬 쓰레드인지 확인한다. 데몬쓰레드일시 TRUE 반환
  • setDaemon(boolean on) : 쓰레드를 데몬쓰레드 혹은 일반 쓰레드로 변경, 설정한다. true 일시 데몬쓰레드가 된다.
  • setDaemon은 반드시 start()를 호출하기전에 작성되어야하며 그렇지않을시 에러를 반환한다.

 

 

 

  • Runnable 인터페이스 구현 시 run 메소드가 있는 러너블 인터페이스를 객체생성하여 Thread 에 대입해야 한다.
  • setDaemon() 이 true 이며 start() 보다 앞서있음에 유의한다. 
  • 무한루프가 작성된 데몬쓰레드는 일반쓰레드가 종료되면 함께 종료된다. 
  • 3초마다 변수 autoSave의 값을 확인해서 그 값이 true이면, autoSave()를 호출하는 일 을 무한히 반복하도록 작성된 쓰레드이다. 
  • 만일 이 쓰레드를 데몬 쓰레드로 설정하지 않았다면, 이 프로그램은 강제종료하지 않는 한 영원히 종료되지 않을 것이다

 

 

  • setDaemon이 start() 이전에 작성되지않으면 보조쓰레드가 아닌 일반쓰레드로 간주된다. 
  • 즉, 일반쓰레드는 별개의 쓰레드 이므로 일반쓰레드가 종료된 이후에도 종료되지않고 계속 돌게된다.
  • setDaemon메서드는 반드시 start()를 호출하기 전에 실행되어야한다.
  • 그렇지 않으면 IllegalThreadStateException 이 발생한다. 

 

 

 

쓰레드의 상태


  • 쓰레드 프로그래밍이 어려운 이유는 동기화(synchronization)와 스케줄링(scheduling) 때문이다.
  • 효율적인 멀티쓰레드 프로그램을 만들기 위해서는 보다 정교한 스케줄링을 통해 프로세스에게 주어진 자원과 시간을 여러 쓰레드가 낭비없이 잘 사용하도록 프로그래밍 해야 한다.
  • 쓰레드의 상태는 NEW, RUNNABLE, BLOCKED, WATING, TIMED_WAIRING, TERMINATED 가 있다.
  • 생성(NEW)된 쓰레드는 > START() 가 되어야 실행대기(RUNNABLE) 상태에 놓이고 > 차례로 실행되게 된다. > 이후 실행대기와 실행을 반복하다가 > STOP()과같은 상태가 떨어지면 > 소멸(TERMINATED) 하게 된다. 

 

 

  • 이때 쓰레드를 일종의 일시정지 상태에 놓을 수 있는데 바로 WAITING과 BLOCKED 이다.
  • 일시정지(suspend), 잠자기(sleep), 기다리기(wait, join), 입출력 대기(I/O block)등이 있다.
  • TIMED_WAITING은 시간이 미리 정해진 일시정지이다.

 

 

  • 일시정지와 대비되는 것은 재개(time-out, resume, notify)와 깨우기(interrupt) 등이다.

 

더보기

 

① 쓰레드를 생성하고 start()를 호출하면 바로 실행되는 것이 아니라 실행대기열에 저장되어 자신의 차례가 될 때까지 기다려야 한다. 실행대기열은 큐 (queue)와 같은 구조로 먼저 실행대기 열에 들어온 쓰레드가 먼저 실행된다.

 

② 실행대기상태에 있다가 자신의 차례가 되면 실행상태가 된다.

 

③ 주어진 실행시간이 다되거나 yield()를 만나면 다시 실행대기상태가 되고 다음 차례의 쓰레드가 실행상태가 된다.

 

④ 실행 중에 suspend(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 될 수 있다. I/O block은 입출력작업에서 발생하는 지연상태를 말한다. 사용자의 입력을 기다리는 경우를 예로 들 수 있는데,이런 경우 일시정지 상태에 있다가 사용자가 입력을 마치면 다시 실행대기 상태가 된다.

 

⑤ 지정된 일시정지시간이 다되거나(time-out), notify(), resume(), in te rrup t)가 호출되면 일시 정지상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다리게 된다.

 

⑥ 실행을 모두 마치거나 stop( )이 호출되면 쓰레드는 소멸된다.

 

 

 

 

 

 

 

 

 

 

쓰레드의 실행제어 메서드 종류


  • 유일하게 static 메서드인 sleep() 과 yield() : 쓰레드 자기 자신에게만 호출이 가능하다.
  • 이는 자기 자신에게만 호출이 가능함을 의미한다.
  • 즉, 자거나 양보하는건 자기 스스로에게만 적용되는 것
  • 쓰레드의 스케줄링을 잘하기 위해서는 쓰레드의 상태와 관련 메서드를 잘 알아야 한다. 

 

 

sleep(long millis)


  • 일정시간동안 쓰레드를 엄추게 한다.
  • sleep()에 의해 일시정지 상태가 된 쓰레드는 지정된 시간이 다 되거나 interrupt()가 호출되면(InterruptedException발생), 잠에서 깨어나 실행대기 상태가 된다.
  • 그래서 sleepO을 호출할 때는 항상 try-catch문으로 예외를 처리해줘야 한다. 
  • static 메서드로 현재 쓰레드에서만 동작한다.

 

interrupt()와 interrupted() 


  • 쓰레드의 작업을 취소한다.
  • 진행 중인 쓰레드의 작업이 끝나기 전에 취소시켜야할 때 사용한다.
  • interrupt()는 쓰레드에게 작업을 멈추라고 요청한다.
  • 단지 멈추라고 요청 만 하는 것일 뿐 쓰레드를 강제로 종료시키지는 못한다.
  • interrupt()는 그저 쓰레드의 interrupted상태(인스턴스 변수)를 바꾸는 것일 뿐이다.

 

suspend( ), resume( ), stop ()


  • suspend()는 sleep()처럼 쓰레드를 멈추게 한다.
  • suspend()에 의해 정지된 쓰레드는 resume()을 호출해야 다시 실행대기 상태가 된다.
  • stop()은 호출되는 즉시 쓰레드가 종 료된다.
  • 이 메서드들은 교착상태 (deadlock)를 일으키기 쉽게 작성되어있으므로 사용이 권장되지 않는다.
  • 그래서 이 메서드들은 모두 ‘deprecated’되었다. (권장이지 강제는 아님)

 

yield()


  • 다른 쓰레드에게 양보한다.
  • yield()는 쓰레드 자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보(yield)한다.
  • static 메서드로 현재 쓰레드에서만 동작한다.

 

join()


  • 다른 쓰레드의 작업을 기다린다.
  • 쓰레드 자신이 하던 작업을 잠시 멈추고 다른 쓰레드가 지정된 시간동안 작업을 수행하도 록할 때 join()을 사용한다
  • 시간을 지정하지 않으면, 해당 쓰레드가 작업을 모두 마칠 때까지 기다리게 된다.
  • join()도 sleep()처럼 interrupt()에 의해 대기상태에서 벗어날 수 있으며, join()이 호출 되는 부분을 try-catch문으로 감싸야 한다.
  • join( )은 여러모로 sleep( )과 유사한 점이 많다.
  • sleep()과 다른 점은 join()은 현재 쓰레드가 아닌 특정 쓰레드에 대해 동작하므로 static메서드가 아니라는 것이다.
  • * sleep 과 yield은 static 메서드로 현재 쓰레드에서만 동작한다.

 

 

 

 

 

* 참고하면 좋은 글

- https://velog.io/@yummygyudon/JAVA-쓰레드-Thread-7luq8ydu

 

[JAVA] 쓰레드 ( Thread ) ④

🏃‍♂️ 들어가기 앞서.. > 본 게시물은 스터디 활동 중에 작성한 게시물로 자바의 정석-기초편 교재를 학습하여 정리하는 글입니다. ※ 스터디 Page : 〔투 비 마스터 : 자바〕 **해당 교재의

velog.io

 

 

 

 

 

쓰레드의 우선순위


  • 쓰레드는 우선순위(priority)라는 속성(멤버변수)을 가지고 있는데, 이 우선순위의 값에 따라 쓰레드가 얻는 실행시간이 달라진다.
  • 쓰레드가 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 서로 다르게 지정하여 특정 쓰레드가 더 많은 작업시간을 갖도록 할 수 있다.
  • 쓰레드가 가질 수 있는 우선순위의 범위는 1〜 10이며 숫자가 높을수록 우선순위가 높다
  • setPriority() : 쓰레드의 우선순위를 지정한 값으로 임의로 설정한다.
  • getPriority() : 알고자하는 쓰레드의 우선순위를 값으로 가져온다.
void setPriority (int newPriority) 	// 쓰레드의우선순위를 지정한값으로 변경한다. 
int get Priority() 		// 쓰레드의 우선순위를 반환한다 •

 

  • 우선순위가 설정되지않은 쓰레드는 기본적으로 5의 우선순위를 갖는다. 
  • 쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속받는다. main메서드를 수행하는 쓰레드는 우선순위가 5이므로 main메서드 내에서 생성하는 쓰레드의 우선순위는 자동적으로 5가 된다.
  • 다만 이는 자바 가상 머신(JVM) 에 의한 우선순위 '희망' 일뿐이지 반드시 우선순위를 갖는 건 아니라는 점을 기억해야 한다. 
  • OS 스케줄러가 참고하는, 그저 확인할 수 있는 정도의 우선순위일뿐 실제적인 우선순위는 그때그때 달라진다. 
  • 그저 쓰레드에 높은 우선순위를 주면 더 많은 실행시간과 실행기회를 갖게 될 것이라고 기대하는 것이다.
  • 차라리 쓰레드에 우선순위를 부여하는 대신 작업에 우선순위를 두어 PriorityQueue에 저장해 놓고, 우선순위가 높은 작업이 먼저 처리되도록 하는 것이 나을 수 있다
  • A의 우선순위가 높은 경우 바의 길이와 순서를 살펴보면 좀더 길고, 앞서며 A의 작업이 더 먼저 끝난다. 

 

 

  • 우선순위를 설정하지않은 쓰레드 th1의 우선순위는 기본적으로 5로 설정되어있다.
  • th1에비해 th2의 우선순위가 높음에도 콘솔창을 보면 th2가 더 늦게 끝난다.
  • 앞서 본 것처럼 우선순위가 희망사항일뿐 항상 반영되는 것은 아니란 점을 확인할 수 있다.

 

 

  • 정말 우선순위를 설정하고싶다면 작업관리자에서 직접 설정해줄 수 있다. 
  • ctrl+alt+Del 작업관리자 실행 후 > 세부정보 탭 클릭 > 마우스 우클릭 > 우선순위 설정

 

 

 

 

 

쓰레드 그룹 


  • 쓰레드 그룹은 서로 관련된 쓰레드를 그룹으로 다루기 위한 것으로, 폴더를 생성해서 관련된 파일들을 함께 넣어서 관리하는 것처럼 쓰레드 그룹을 생성해서 쓰레드를 그룹으로 묶어서 관리할 수 있다.
  • 모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 한다.
  • 쓰레드 그룹을 지정하는 생성자를 사용하지 않은 쓰레드는 기본적으로 자신을 생성한 쓰레드와 같은 쓰레드 그룹에 속하게 된다
  • 그동안 그룹을 설정하지않고 쓰레드를 사용한 것은 main 쓰레드 그룹에 속해있었기 때문이다. 
  • 우리가 생성하는 모든 쓰레드 그룹은 main쓰레드 그룹의 하위 쓰레드 그룹이 되며, 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 자동적으로 main쓰레드 그룹에 속하게 된 다.
  • 쓰레드는 자신을 생성한 쓰레드의 그룹과 우선순위를 상속받는다. 
  • getThreadGroup() : 속해있는 쓰레드 그룹을 반환하여 알아보고자 할때 사용

 

 

 

 

 

쓰레드 그룹의 메서드 


 

 

 

 

 

 

 

 

 

 

 

 

 

main 쓰레드


  • 프로그램 실행시 익히 사용해서 아는 main() 메소드역시 쓰레드의 일종이다. 
  • 즉 main 메서드는 main 에서의 코드를 수행하는 쓰레드다. 
  • 쓰레드의 종류엔 1. 사용자 쓰레드와 2. 데몬 쓰레드 두가지가 있다. 
  • 우리가 기억해야 할 점은 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다는 것이다. 

 

 

  • 다음 예제에서 main 메서드내 th1, th2 쓰레드가 실행되고 있다. 
  • 이때 콘솔창을 눈여겨 보면 main 메소드가 먼저 실행되고 두개의 스레드가 차례로 번갈아 실행됨을 확인할 수 있다. 
  • 이때 join() 메소드가 주석된 부분을 확인할 수 있는데, 해당메소드는 main 쓰레드로 하여금 다른 쓰레드들의 작업이 끝날때까지 기다리도록 하는 기능이다. 
  • 더이상 실행될 쓰레드가 없다면 프로그램은 종료된다. 

 

 

  • 여기서 join() 메소드를 주석처리시켜 알 수 있는 점은 
  • main 메소드가 끝난다고 해서 프로그램이 종료되는 것은 아니라는 점이다. 
  • 모든 쓰레드가 종료되고 나서야 프로그램은 비로소 종료된다. 
  • main 쓰레드는 그 여러 쓰레드 중 하나일 뿐이다. 

메인메서드의 종료가 프로그램의 종료를 의미하지 않는다.

 

 

  • join 메서드의 주석을 해제 한다면 다음과 같은 것을 콘솔에서 확인할 수 있다.
  • 모든 쓰레드가 차례로 실행된뒤 마지막에 main 메서드가 출력된 후 프로그램이 종료된다.  

 

 

 

 

 

싱글쓰레드와 멀티쓰레드


  • 싱글 쓰레드는 하나의 메서드 내에서 다음과같이 실행된다.
  • A와 B의 순서와 실행 시간이 일정하다. 

 

  • 다음과같이 예제에서도 싱글 쓰레드는 각각의 메서드의 실행순서가 순차적이며 소요시간이 비슷한 것을 확인할 수 있다.
  • 실행 순서는 하나가 끝나야 다른 하나가 실행되기에 절대 작업 순서간의 겹치는 일이 발생하지않는다. 

 

 

 

  • 하나의 쓰레드로 두 작업을 처리하는 경우는 한 작업을 마친 후 에 다른 작업을 시작하지만, 두 개의 쓰레드로 작업 하는 경우에는 짧은 시간동안 2개의 쓰레드(thl, th2)가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리된다.
  • 멀티스레드는 싱글 스레드와 달리 실행순서와 소요 시간 등을 OS 스케줄러가 결정한다는 차이점이 있다.
  • 그래프를 보면 막대의 끝을 비교했을때 소요 시간도 조금씩 차이가 나는 것을 확인할 수 있다. 
  • 대체적으로 멀티쓰레드의 소요시간이 조금 더 걸린다. 
  • 이는 작업이 번갈아 일어날때 발생하는 Context Switch(작업 전환) 때문이다. 
  • 작업 전환을 할 때는 현재 진행 중인 작업의 상태, 예를 들면 다음에 실행해야할 위치 (PC, 프로그램 카운터) 등의 정보를 저장하고 읽어 오는 시간이 소요된다.
  • 그래서 싱글 코어에서 단순히 CPU만을 사용하는 계산작업이라면 오히려 멀티쓰레드보다 싱글쓰레드로 프로그래밍하는 것이 더 효율적이다.

 

 

  • 멀티 쓰레드의 예제를 보면 두 작업이 아주 짧은 시간동안 번갈아가면서 실행되었으며 거의 동시에 작업이 완료되었음을 알 수 있다.
  • 즉, 작업수행 순서, 시간 모두 균일하지 않음을 확인할 수 있다. 
  • 여기서 실행할 때마다 다른 결과를 얻을 수 있는데 그 이유는 실행 중인 예제프로그 램(프로세스)이 OS의 프로세스 스케줄러의 영향을 받기 때문이다.
  • 자바가 OS(플랫폼) 독립적이라고 하지만 실제로는 os종속적인 부분이 몇 가지 있는데 쓰레드도 그 중의 하나이다

 

 

 

 

쓰레드의 I/O 블락킹


  • I/O 블락킹이란 입출력시 작업이 중단되는 것을 의미한다. 
  • I/O 블락킹을 통해 사용자로부터 작업을 기다리는 구간에서 다른 작업이 일어나는 지의 여부를 살펴볼 수 있다. 
  • A에서는 사용자로부터 작업을 기다리는 로직을, B에서는 카운트 다운을 세는 로직을 만든다.
  • 같은 메소드를 싱글, 멀티 쓰레드로 구현했을때 
  • 싱글쓰레드는 하나의 작업이 끝난후 다음 작업이 일어나야 하므로 사용자의 입력을 기다리는 동안 해당구간에서는 아무일도 일어나지 않는다. 
  • 멀티 쓰레드는 여러 쓰레드가 번갈아 가며 작업이 전환되므로, A에서 사용자의 작업을 기다리는 동안 B에서 카운트 다운이 계속 세어지는 것을 확인할 수 있다. 
  • 즉, 싱글스레드에서는 I/O 블락킹이 발생하지만 멀티스레드에서는 그렇지 않다. 

 

 

 

  • 하나의 쓰레드로 처리한다면 첫 번째 그래프처럼 사용자가 입력을 마칠 때까지 아무 일도 하지 못하고 기다리기만 해야 한다.
  • 그러나 두 개의 쓰레드로 처리한다면 사용자의 입력을 기다리는 동안 다른 쓰레드가 작업을 처리할 수 있기 때문에 보다 효율적인 CPU의 사용이 가능하다
  • 결국 작업시간에 있어서 B, 즉 멀티쓰레드가 효율적이다. 
  • 싱글쓰레드에서 I/O 블락킹이 발생해 다른 작업들이 멈춰있는 정지 구간동안 멀티쓰레드는 B가 실행될 수 있기 때문이다. 

 

  • 두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글쓰레드 프로세스보다 멀티 쓰레드 프로세스가 더 효율적이다.
  • 예를 들면 사용자로부터 데이터를 입력받는 작업,네트워크로 파일을 주고받는 작업,프린터로 파일을 출력하는 작업과 같이 외부기기와의 입 출력을 필요로 하는 경우가 이에 해당한다

 

 

 

 

  • 싱글 쓰레드의 경우 입력값을 받는 로직이 끝날때까지 다른 작업을 실행되지않는다.
  • 즉 싱글쓰레드에서는  I/O 블락킹이 발생한다. 

 

  • 얼마나 걸리든, A의 실행(값 입력)이 끝나고 나서야 B(카운트 다운)가 실행될 수 있다. 

 

 

  • 멀티 쓰레드 작성시 입력값을 입력하지 않았지만 카운트 다운 쓰레드가 실행되고있다. 
  • 즉 동시진행되고 있어 I/O 블락킹이 발생하지않았음을 확인할 수 있다. 
  • 다른작업을 진행할 수 있으므로 동시 작업이 필요한 프로그램에 효율적이다. 

 

 

 

 

 

 

 

 

 

 

 

 


쓰레드의 구현과 실행


  • 쓰레드를 구현하는 방법은 1. Tread 클래스를 상속하는 것과 2. Runnable 인터페이스를 구현하는 방법이 있는데 
  • Tread 클래스를 상속할 경우 다른 클래스의 상속을 못 받기 때문에 Runnable 인터페이스를 구현하는 방법을 일반적으로 사용한다. 
  • 알다시피 자바는 단일 상속만을 지원하기 때문이다. 

 

 

  • main 메서드를 채워주듯이 쓰래드 내에서 run() 메서드를 구현해주면 된다. 
  • 1번 방법은 첫번째 박스와같이 스레드를 생성 > 곧바로 start() 를 호출하여 실행하면 된다. 
  • 2번 방법은 두번째 박스와 같이 Runnable 인터페이스를 구현 후 start() 메서드를 호출한다.
  • Runnable인터페이스를 구현하기 위해서 해야 할 일은 추상메서드인 run()의 몸통{ }을 만들어 주는 것뿐이다.
  • 쓰레드를 구현한다는 것은, 위의 두 방법 중 어떤 것을 선택하든지, 그저 쓰레드를 통해 작업하고자 하는 내용으로 run()의 몸통({ })을 채우는 것일 뿐이다.
  • Runnable인터페이스를 구현한 경우,Runnable인터페이스를 구현한 클래스의 인스턴스 를 생성한 다음,이 인스턴스를 Thread클래스의 생성자의 매개변수로 제공해야 한다.

 

 

 

 

 

  • 예제에서 두가지 방법으로 쓰레드를 구현하였다. 
  • 방법이 달라도 run() 메소드 내에 쓰레드가 수행할 작업을 작성하는 것은 동일하다. 
  • run() 메소드 대신 start() 메소드를 실행하는 것에 유의한다. 
  • Tread 메소드를 상속하여 작성한 쓰래드는 현재 실행중인 쓰래드의 이름을 알고싶을때 this.getName() 만으로 손쉽게 호출할 수 있다. 
  • Runnable 인터페이스를 구현하여 작성한 쓰래드는 Tread.currentTread().getName 과 같이 현재 실행중인 스레드 객체를 불러와 이름을 호출해야하는 것을 알 수 있다. 

  • Thread클래스를 상속받으면, 자손 클래스에서 조상인 Thread클래스의 메서드를 직접 호출할 수 있지만, Runnable을 구현하면 Thread클래스의 static메서드인 current ThreadO를 호출하여 쓰레드에 대한 참조를 얻어 와야만 호출이 가능하다.
  • Thread를 상속받은 ThreadExl_1에서는 간단히 getName()을 호출하면 되지만, Runnable을 구현한 ThreadExl_2에는 멤버라고는 run()밖에 없기 때문에 Thread클래 스의 getName()을 호출하려면, ‘Thread.currentThread().getName()' 와 같이 해야한다.
  • 쓰레드의 이름을 지정하지 않으면 Tliread-번호’ 의 형식으로 이름이 정해진다.

 

 

 

 

  • 쓰레드를 생성했다고 해서 자동으로 실행되는 것은 아니며 startO를 호출해야만 쓰레드가 실행된다.
  • 사실은 startO가 호출되었다고 해서 바로 실행되는 것이 아니라, 일단 실행대기 상태에 있다가 자신의 차례가 되어야 실행된다. 물론 실행대기중인 쓰레드가 하나도 없으면 곧바 로 실행상태가 된다.
  • 작성한 쓰레드는 멀티 스레드로서 두가지 스레드가 동시에 실행되는데 데이터를 넓혀보면 다음과같은결과를 확인할 수 있다. 
  • 작성순서와 길이 등 우선순위가 실행시마다 다르게 출력됨을 알 수 있다. 
  • 이는 OS 의 스케줄러가 어떤 스래드를 실행할지 정하기 때문이다. 

 

만약 콘솔이 한줄로 나온다면 이미지속 버튼을 클릭하여 줄바꿈 한다.

 

  • 한 가지 더 알아 두어야 하는 것은 한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다는 것이다.
  • 즉, 하나의 쓰레드에 대해 start( )가 한 번만 호출될 수 있다는 뜻이다.
  • 그래서 만일 쓰레드의 작업을 한 번 더 수행해야 한다면 새로운 쓰레드를 생성한 다음에 start()를 호출해야 한다.

 

 

 

 

  • 멀티스레드로 작성된 내용을 주석처리 후 싱글 스레드 작성로 변경 시 출력결과를 보자 
  • 하나의 작업(0)이 끝나야 다음작업(1)이 실행되는 것을 확인할 수 있다.

 

 

  • 그러나 멀티스레드로 작성시 두 작업이 번갈아 실행된다.
  • 어떤 작업이 먼저, 얼마나 실행될지는 앞서 기술한대로 OS 스케줄러에 따라 결정된다. 

 

 

 

 

쓰레드의 실행 - start()


  • main메서드에서 run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순 히 클래스에 선언된 메서드를 호출하는 것일 뿐이다
  • 반면에 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택(call stack)을 생 성한 다음에 run()을 호출해서, 생성된 호출스택에 run()이 첫 번째로 올라가게 한다.
  • 모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문 에,새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종 료되면 작업에 사용된 호출스택은 소멸된다.

 

 

 

 

  • 쓰래드를 구현한 후 작성한 메소드는 run()이었지만 다음과같이 호출시엔 start() 메서드를 사용한다. 
  • 쓰래드의 실행전 다음과같은 두가 사항을 기억해야 한다. 
  • 1. 이 start() 메서드를 호출하는 것이 즉시 실행을 의미하는 것은 아니라는 것
  • 2. 또한 먼저 작성한 메서드가 먼저 실행되는 것도 아니라는 것

 

 

 

  • 예제 속 콘솔창을 보자. 
  • 두번째 줄에 작성한 t2.start() 가 먼저 실행되어 0보다 1이 먼저 출력되었으며 
  • 0과 1을 얼마나 출력할지, 얼마나 지속할지 일정하게 정해진 것이 아니라는 점을 확인할 수 있다. 

 

 

  • 우리는 알게모르게 쓰레드를 사용해왔다. 바로 main 메서드이다. 
  • start() 메소드는 쓰레드의 즉시 실행을 의미하지 않는다고 했다. 
  • 두번째 그림 2.Call stack 에서 확인할 수 있듯 '새로운 호출스택을 생성' 해 줄 뿐이다. 
  • 이는 해당 스택에서 run() 메소드가 실행될 수 있음을 의미한다. 
  • main 메서드가 기본 쓰레드를 생성후 start() 메서드로 새로운 호출스택을 생성한다.
  • 이렇게 start() 로 인해 생성된 스택에서 또다른 쓰레드(run 쓰레드)는 이전의 (main 쓰레드)와는 별개로 독립적인 작업을 수행할 수 있는 것이다. 

  • 위의 그림에서와 같이 쓰레드가 둘 이상일 때는 호출스택의 최상위에 있는 메서드일지라도 대기상태에 있을 수 있다.
  • 스케줄러는 실행대기중인 쓰레드들의 우선순위를 고려하여 실행순서와 실행시간을 결정하고,
  • 각 쓰레드들은 작성된 스케줄에 따라 자신의 순서가 되면 지정된 시간동안 작업 을 수행한다.
  • 지금까지는 main메서드가 수행을 마치면 프로그램이 종료되었으나, 위의 그림에서와 같 이 main메서드가 수행을 마쳤다하더라도 다른 쓰레드가 아직 작업을 마치지 않은 상태라면 프로그램이 종료되지 않는다.

 

 

 

 

 

 

 

조금더 정리된 내용을 알고싶다면 아래 링크를 참조하면 좋을 것이다.

 

http://www.tcpschool.com/java/java_thread_concept

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

 

 

 

  • 서블릿과 JSP 는 사용 목적이 비즈니스 로직과 화면로직을 분리하기 위함에 있었다. 
  • JSP 내에서도 여러 문법이 있었는데, 자바 언어를 모르더라도 XML을 확장한 문법으로 간편하게 활용할 수 있는 방법을  알아보도록 한다. 
  • 프로젝트를 생성한다. 

 

 

  • 이때 인덱스 파일은 JSP로 만들어보도록 한다. 

 

 

 

  • 이때 web.xml에 들어가 보면 다음과같은 웰컴 파일들을 확인 해 볼 수 있다. 
  • 여기 적힌 순서대로 찾아서 웰컴파일로 삼는 것이다. 
  • 주로 index 혹은 default 등으로 설정 한다.  
  • 이때 index.jsp 도 웰컴파일로 사용할 수 있도록 설정이 되어있는걸 확인할 수 있다. 

 

 

 

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>el-jstl</title>
</head>
<body>
	<h1 align="center">JSP Action Tag</h1>
	<h3><a href="views/action/testAction.jsp">JSP Action Tag 테스트</a></h3>
	
	<br>
	
	<h2>EL test</h2>
	<h3><a href="test1">request.getAttribute() 테스트</a></h3>
	<h3><a href="test2">request에 저장 된 객체 출력 테스트</a></h3>
	<h3><a href="views/el/testEl3.jsp?name=galaxy&price=95&no=5&no=6&option=삼성">parameter로 값 전달한 경우 el 테스트</a></h3>
	<h3><a href="test4">requestScope와 sessionScope 테스트</a></h3>
	
	<br>
	
	<h2>JSTL test</h2>	
	<h3><a href="views/jstl/testJstlCore.jsp">Core Library Test</a></h3>
	<h3><a href="views/jstl/testJstlFmt.jsp">Fmt Library Test</a></h3>
	<h3><a href="views/jstl/testFunction.jsp">Function Library Test</a></h3>
</body>
</html>

 

 

 

 

JSP Action Tag


  • 1.JSP 액션 태그 2. EL 3. JSTL 의 순으로 학습한다. 
  • 꼭 자바 문법을 모르더라도 쉽게 사용할수 있도록, 혹은 좀더 간결하게 사용할 수 있도록 고안된 문법이다. 
  • XML을 확장한 태그 형식의 문법이다.  

 

  • 경로맨 앞에 슬래쉬(/)를 붙이지 않는 것은 상대경로임을 의미한다. 
  • 즉 상대경로는 최상위 루트인 web에서부터 시작하게 된다.   

  • 그런데 이때 testAction.jsp 에서 다시 index.jsp 로 돌아가고싶을땐 어떻게 해야 하는가?
  • 아마 ../../../ 이와같이 작성해서 경로를 거슬러 올라가야 할 것이다. 
  • 이때 번거로움을 줄이고자 사용하는게 절대경로이다. 
  • 맨 앞에 슬래쉬를 붙이면 이를 절대적인 경로로 해석하게 된다. 이때는 chap14라는 경로부터 시작해야한다. 

 

 

  • 정리하자면, 상대경로를 쓰고싶다면 다음과같이 맨 앞에 슬래쉬를 빼면 되고 

  • 절대경로로 작성하고자 한다면 맨앞에 슬래쉬를 붙여 경로를 픽스 해 두고 하위 경로를 작성하면 된다. 

 

  • 작성한 경로에 맞게 파일을 생성해준다. 

 

 

 

  • testAction 내에 include를 표현하고자 한다. 
  • include 를 표현하려면 include 하려는대상파일이 있어야 하므로 새로 만들어준다. 
  • common.jsp 파일을 만들어준다. 

 

 

common.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>안녕하세요? common.jsp 입니다!</h1>
</body>
</html>

 

  • 다시 testActon.jsp 파일로 돌아와 inclde를 위해 지시자 태그를 작성한다. 
  • 지시자 태그는 다음과 같이 썼다. <%@ %>
  • common.jsp 는 경로로 따졌을때 같은 폴더 내에 있으므로 상대경로서 슬래쉬 없이 다음과같이 작성해준다.  

 

 

  • 확인을 위해선 서버를 올려 지시자 태그가 잘 올라가는지 확인해야 한다. 
  • 해당 서버를 우클릭 >  Add and Remove > configured 에 chap14를 추가 > Finish

 

 

  • 다음과 같이 뜬다면 잘 include 되었으며 상대경로 역시 잘 작성된 것이다. 

 

 

  • 이렇게 지시자 태그를 사용하지않고, 액션태그를 이용해서 인클루드를 표현하는 방법도 있다.  

 

  • 다음과 같이 작성한다. 
  • <jsp:include page="경로"></jsp:include>
  • 액션태그는 xml의 문법을 확장해서 만든 태그이며, 태그 내 열고 닫는 부분을 맞춰주는 것이 보다 엄격하다. 
  • 그래서 여닫는 태그가 잘 적용해 짝 지어졌는지 반드시 확인해야 한다. 

 

<!-- JSP Action Tag의 경우 여닫는 태그 반드시 작성해야 함 -->
	<jsp:include page="common.jsp"/>

 

 

  • 닫는 태그 누락시 해당페이지는 500 Error가 발생한다.
  • <>태그와 태그사이<>에 무언가를 작성하고 싶은게 아니라면, 그냥 태그 뒤에 </>만 작성해 주면 된다.  

 

 

  • 스크립틀릿 태그 내에선 간단한 자바코드를 작성할 수 있다고 했다. 
  • 다음과 같이 작성 후 포워딩을 위해 "testForward.jsp" 경로를 실제로 작성해준다.

 

  • 앞서 request 객체에 setAttribute 해 주었다. 
  • 그말은 즉, 설정된 값을 가져오는 getAttribute 도 가능하게 된다는 말이 된다. 
  • 값을 가져오며, 가져온 값을 표현하기 위해 익스프레션 태그를 사용한다. <%= %>

 

 

  • 실제 테스트 후 주소를 살펴보면, 실제 경로는 view/action/testAction.jsp 로 갔지만
  • 실제적으론 foward 라는 코드를 통해서 testForward 쪽으로 포워딩된 결과를 확인할 수 있다. 
  • 또한 setAttribute로 설정된 홍길동을 getAttribute로 가져오는 것도 확인할 수 있다. 

 

 

 

  • 이렇게 자바코드를 작성할 수 있지만, jsp 액션태그로도 주석처리 된 이 두줄을 작성할 수 있다. 

 

  • 다음과 같이 작성해 줄 수 있다. 
<jsp:forward page="포워딩할/페이지/경로.jsp"/>

 

  • 자바코드로도 작성할 수 있지만 jsp 는 뷰적인 영역이고 뷰에 목적이 있기 때문에
  • 액션 태그와 같은 문법들을 사용할 수 있도록 만들어 놓은 것이다. 
  • testForward.jsp 화면에서 몇가지만 더 확인해보도록 한다. 

 

 

 

 

  • jsp:useBean 활용하기 
  • jsp:useBean 을 활용하기 위해선 값을 설정해줄 하나의 DTO  클래스가 필요하다.

 

  • 서블릿 파일 작성

 

 

 

 

  • DTO 클래스 작성 후 나머지 get, set 메소드를 작성해 주어야 한다. 

 

 

1) Generate Constructor using Fields

마우스 우클릭 > Source > Generate Constructor using Fields > Generate

 

 

 

2) Generate Getters and Setters

마우스 우클릭 > Source > Generate Getters and Setters > Select All > Generate

 

 

3) Generate toString()...

마우스 우클릭 > Source > Generate toString() > Generate

 

 

  • 다시 testForward 로 돌아와 작성한다. 
  • <jsp ... 만 작성하였는데도 굉장히 사용할 수 있는 태그가 많은 것을 확인할 수 있다. 
  • 그중에서 마지막으로 테스트 해보고 싶은 것은 useBean 이고, 아이디와 클래스를 차례로 작성한다.
  • 클래스에는 방금 작성한 DTO 의 패키지명을 포함한 클래스명을 기재한다. 

 

  • 참고로 MemberDTO 의 기본생성자는 필수인데, 이를 주석처리 할 시 내부서버 오류의 문제가 발생한다. 

 

 

  • DTO 내 기본생성자를 가져다 쓰는데, 이것이 주석처리 되면 다음과같은 예외가 발생함을 알 수 있다. 
  • DTO 등을 작성할 때 기본생성자는 항상 기본으로, 습관적으로 작성해두어야 하는 이유이다. 
  • 기본적으로 기본생성자를 가져가서 인스턴스를 생성하는 처리가 다양히 존재하기 때문이다. 
  • useBean은 "member"라는 이름을 가진 인스턴스를 만들자, 라는 것

 

 

 

 

 

<jsp:setProperty> 


  • userBean에 id 로 준것은 그것이 해당 인스턴스의 name 이기 때문이다. 
  • 1) 즉, userBean 의 id는 name 의 역할을 하며, setProperty 내의 name 과 동일해야 한다. 
  • 2) property는 작성했던 dto 내에서 값을 설정해줄 컬럼이다. 
  • 3) 해당 property 가 설정되었으면 하는 값을 value에 설정해 준다. 

 

  • 해당 인스턴스의 각각의 속성에다 원하는 값을 설정 해 주었다. 
  • 이것이 잘 설정되었는지 눈으로 확인하고 싶다면 getProperty를 통해 가져오면 된다. 
  • 마찬가지로 member 라는 인스턴스내의 속성임을 표현하기 위해 name 에 인스턴스 명을 작성한다. 
  • name : 해당 인스턴스를 나타내는 명
  • property : 인스턴스의 필드 값 

 

 

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1><%= request.getAttribute("name") %>님 환영합니다.</h1>
	
	<h2>jsp:useBean 활용하기</h2>
	<jsp:useBean id="member" class="com.greedy.el.model.dto.MemberDTO"/>
	<jsp:setProperty property="name" name="member" value="홍길동"/>
	<jsp:setProperty property="age" name="member" value="20"/>
	<jsp:setProperty property="phone" name="member" value="010-1234-5678"/>
	<jsp:setProperty property="email" name="member" value="hong@greedy.com"/>
	
	이름 : <jsp:getProperty property="name" name="member"/> <br>
	나이 : <jsp:getProperty property="age" name="member"/> <br>
	전화번호 : <jsp:getProperty property="phone" name="member"/> <br>
	이메일 : <jsp:getProperty property="email" name="member"/> <br>
	
	
	
</body>
</html>

 

 

 

 

참고

더보기

input Box 속성 name과 id의 차이

 
▶ name
(1) document.폼객체명.폼원소명.value
(2) document.getElementsByName("name")
 
name은 page 안에서 중복되어 사용이 가능하며 action에 해당하는 페이지로 전달하는 파라미터로 사용
GET/POST 방식으로 값을 전달하고 싶은 tag에 사용. Form 객체들(input , radio box ,checkbox )에서 전송되어지는 Parameter의 Key값으로 사용
서버단에서는 request.getParameter(parameterName) 이런 식으로 값을 가져옴
 
 
▶ id
(1) document.all.id.value
(2) id.value
(3) document.getElementById("폼 id").value
 
id는 page 안에서 중복 사용 불가하며 주로 JavaScript에서 다룰려고 지정.
name도 자바스크립트로 속성이나 값을 변경 할 수 있으나 중복값을 갖기 때문에 id로 접근하는 것을 주로 사용.
document.getElementById(id) 를 통해서 해당 엘리먼트Object 를 가져옴.
 
XML문서(DOM Tree) 내에서 node를 식별하는 용도로 사용되어 페이지 안에 이름이 한개만 존재해야 함.
보통은 특정 tag를 지정하기 위해서 사용. 태그 내용을 바꾸는 dhtml 작업에서 주로 사용.
 
참고로 id로 설정된 값은 서버쪽으로 파라미터로 넘어가지 않기 때문에 서버쪽에서 접근이 안됨.
 
 
 

input 박스 name과 id의 차이

input Box 속성 name과 id의 차이 ▶ name(1) document.폼객체명.폼원소명.value(2) document.getElementsByName("name") name은 page 안에서 중복되어 사용이 가능하며 action에 해당하는 페이지로 전달하는 파라미터로 사

devfootprint.tistory.com

 

 

 

(+) 참고하면 좋은 글 

https://velog.io/@ansalstmd/JSP4.-%EC%95%A1%EC%85%98-%ED%83%9C%EA%B7%B8

 

 

 

 

 

 

 

'Programming > Servlet&JSP' 카테고리의 다른 글

EL-JSTL 실습  (0) 2023.04.24
JSP 태그 종류와 사용법(2)  (0) 2023.03.30
JSP 태그 종류와 사용법(1)  (0) 2023.03.29
10. EL & JSTL (2)  (0) 2022.03.08
10. EL & JSTL (1)  (0) 2022.03.08

 

JSP 태그 종류와 사용법(1)

https://0306-0903.tistory.com/202

 

JSP 태그 종류와 사용법(1)

들어가며 앞서 JSP 태그 종류를 살펴보았고, 이번 포스팅에서는 각각의 태그의 쓰임을 좀 더 자세히 살펴본다. https://0306-0903.tistory.com/118 8. JSP(Java Server Page) (2) JSP 내장 객체 JSP 내장 객체란? JSP에

0306-0903.tistory.com

 

 

 

 

  • 앞서 언급한 내용 중 지시자 태그는 page 지시자 태그, include 지시자 태그, taglib 지시자 태그가 있다.
  1. page 지시자 태그 : 현 jsp 페이지에 대한 설정을 하는 지시자 태그
  2. include 지시자 태그 : 다른 jsp 파일을 포함하기 위해 사용하는 지시자 태그
  3. taglib : xml 문법 기반의 라이브러리인 JSTL을 이용하기 위해 선언하는 지시자 태그

 

  • 동그라미 친 해당위치에 page, include, taglib 등을 넣어서 사용하는 것이다. 
  • 앞서 (1) 에서 page 지시자를 살펴보았다면, 이제는 include 지시자를 테스트 하기 위해 index 에 페이지를 하나 더 만들어보기로 한다. 

 

 

 

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>jsp</title>
</head>
<body>
	<h1 align="center">JSP</h1>
	<ul>
		<li><a href="jsp/1_simpleJspStates.jsp">JSP 기본 문법</a></li>
		<li><a href="jsp/2_pageDirective.jsp">page 지시자 태그</a></li>
		<li><a href="jsp/3_includeDirective.jsp">include 지시자 태그</a></li>
	</ul>
</body>
</html>

 

3_includeDirective.jsp 페이지를 만들어준다. 

 

 

 

 

  • include 지시자 태그를 이용하여 file 속성에 jsp 경로를 지정해주면 
  • 해당 jsp에 작성한 내용을 그대로 포함시켜 현재 jsp 파일을 동작시킨다.
  • 포함이 시키려면 포함될 jsp 파일이 있어야하므로 포함을 시킬 jsp 파일을 작성한다. 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>3_includeDirective</title>
</head>
<body>
	<h1 align="center">include directive</h1>
	<!-- 
		include 지시자 태그를 이용하여 file 속성에 jsp 경로를 지정해주면 해당 jsp에 작성한 내용을 그대로 포함시켜 현재 jsp 파일을 동작시킨다.
	 -->
	 <div align="right"><%@ include file="today.jsp" %></div>
	 <%
	 	/* 동일한 변수 이름을 사용했기 때문에 컴파일 에러가 발생한다. */
	 	//String output = "";
	 %>
</body>
</html>

 

 

 

  • 또다시 같은 경로에 Today 라고 하는 jsp 파일을 작성한다. 

 

 

 

 

 

 

  • today.jsp 페이지 내 스크립틀릿 코드 안에 DATE 객체를 작성하는데, 이때는 임포트 되지않았으므로 에러가 발생할 것이다. 

 

  • 하나 더 페이지 지시자를 만들어도 문제없다. 

 

  • 그 다음 작성시 또다시 에러가 발생한다. 

 

 

  • 앞서 배웠듯이, import 가 여러개 필요할 때는 콤마(, ) 찍은 뒤 기술하면 된다고 했다.  

 

 

  • SimpleDateFormat 으로 원하는 문자열 포맷을 나타내보자. 
  • format 이라는 메소드를 사용하여 today를 담는다. 
  • 이후 익스프래션 태그를 이용해 화면에 출력하도록 한다. 

 

  • import 전, today 만 보자면, (주소에 경로를 바꿔 입력했을때) 다음과같이 포맷화가 잘 된 시간을 확인할 수 있다. 

 

 

  • 하지만 확인하고 싶은 것은 include 지시자 태그이다.
  • 돌아와서 지시자 태그 내에 inclue 와 파일속성을 사용해 경로를 작성한다. 

 

 

  • 주소 경로를 다시 입력하여 include 지시자 태그를 클릭하면
  • 다음과같이 화면 오른쪽에 포맷화된 날짜를 확인할 수 있다. 

 

 

 

유의할 점은 무엇이 있는가?


  • 만약  포함하는 페이지에서 output 이라는 변수를 사용한다면
  • 다음과 같은 변수의 중복으로 인한 에러메세지가 뜬다.
  • Duplicate local variable output

 

 

왜?

  • 포함되는 today.jsp 영역안에 이미 output 이라는 지역변수가 선언되어 있기 때문이다. 
  • 전체적인 코드안에 '포함되는(include)' 코드들도 공유되어있다. 
  • include 지시자를 통한 포함 처리를 할 때는 하나의 jsp 로 간주되기 때문에 중복된 변수이름을 사용할 수 없다. 

 

 

 

 

 

 

  • 다음으로 jsp를 이용한 응답처리를 테스트 한다.
  • index.html 내에 요청과 응답을 테스트 할 페이지를 만들어 준후 경로에 맞게 jsp 파일을 생성한다. 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>jsp</title>
</head>
<body>
	<h1 align="center">JSP</h1>
	<ul>
		<li><a href="jsp/1_simpleJspStates.jsp">JSP 기본 문법</a></li>
		<li><a href="jsp/2_pageDirective.jsp">page 지시자 태그</a></li>
		<li><a href="jsp/3_includeDirective.jsp">include 지시자 태그</a></li>
		<li><a href="jsp/4_request.jsp">jsp를 이용한 응답 처리(forward)</a></li>
	</ul>
</body>
</html>

 

 

 

 

 

 

  • form 에 action 태그를 준 후  request 즉 내장객체를 사용한다. 
Action 속성
action 속성은 폼이 제출될 때 수행할 행동을 정의 한다.
(서버에 폼을 제출하는 일반적인 방법은 submit  버튼을 사용)
일반적으로, 폼은 웹서버에 있는 웹페이지에 제출된다.
만약 action 속성이 생략되어 있으면, action 은 현재 페이지로 설정된다.
(출처 : http://jun.hansung.ac.kr/CWP/htmls/HTML%20Forms.html)
Method 속성
method 속성은 폼을 제출할 때 사용될 HTTP method (GET or POST)를 지정한다.
(출처 : http://jun.hansung.ac.kr/CWP/htmls/HTML%20Forms.html)

 

  • jsp 안에서 사용할 수 있게끔 몇가지 객체들이 선언되어 있기 때문이다. 
  • 코드속 request객체는 서블릿에서 사용하던 httpServletRequest 인스턴스이다. 

 

  • contextPath는 프로젝트 내에서 chap13 이라고 임의로 설정이 되어있다. 
  • 가장 root 인 경로부터 시작해서 경로를 작성하고자 하는 것이다. 
  • "<%= request.getContextPath() %>/menu/order" chap13/menu/order 와 동일하다. 

 

 

 

  • 인덱스 페이지로 들어가 정말 그러한지 확인해 볼 수 있다. 

 

 

 

  • 마저 테스트를 하기위해 form 내에 태그 까지 작성해준다. 
  • 화면상에서는 다음과같이 확인이 가능하다. 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>4_request</title>
</head>
<body>
	<h1 align="center">메뉴 주문</h1>
	<form action="<%= request.getContextPath() %>/menu/order" method="post">
		<select id="menu" name="menuName">
			<option value="햄버거">햄버거</option>
			<option value="짜장면">짜장면</option>
			<option value="짬뽕">짬뽕</option>
			<option value="순대국">순대국</option>
		</select>
		<input type="number" min="0" max="100" step="1" name="amount">
		<button type="submit">선택 완료</button>
	</form>
</body>
</html>

 

 

 

  • 다음으로, menu/order 라는 요청에 대해서 매핑될 서블릿 클래스가 필요하다.

  • 클래스 작성

 

  • MenuOrderServlet 을 만들어준다. > Next 

 

  • form 태그에 작성한 대로, 요청될 url 매핑을 다음과같이 작성해준다. 

 

 

  • post 방식의 요청이었으므로, 작성할 메소드는 doPost 가 된다. 
  • 이 서블릿안에서 무엇을 해야 할까? 

 

 

 

서블릿에서 하는 일


  1.  요청 받은 값 확인 및 검증
  2.  비지니스 로직 처리
  3.  응답 페이지 생성 후 응답

여기서 3번은 서블릿이 아닌 jsp 로 foward 하여 응답 페이지를 만들어 보도록 한다. 

 

 

  • * doPost 메소드를 수행하면서 한가지 잊지 말아야 하는 점은, 
  • 값을 받아오기 전에 Character Encoding 처리를 먼저 해주어야 한다는 것이다. 
  • 그래야 한글값에 대한 부분이 잘 처리 될 것이다. 

 

 

1. 요청 받은 값 확인 및 검증 

  • 확인 해야 될 값은 다음과 같다 -> 1. 메뉴 이름 2. 메뉴 수량 
  • name 속성으로 확인한다. 

 

 

 

  • name 속성으로 값을 가져오려고 할 때 에러 줄이 발생한다. 왜일까? 

 

  • 항상 넘어오는 통신 값은 문자열로 넘어온다.
  • 그러므로 필요한 다른 형태가 있다면 형변환(Parsing) 해주어야 한다. 

 

 

 

2. 비즈니스 로직 처리 

  • 비즈니스 로직은 대부분 DB에 CRUD 연산 등을 이용해 이루어지게 된다.
  • 여기서는 DB 연결을 할 것은 아니기 때문에 간단한 로직 처리만 해보자.

		int orderPrice = 0;
		
		switch(menuName) {
		case "햄버거" : orderPrice = 6000 * amount; break;
		case "짜장면" : orderPrice = 7000 * amount; break;
		case "짬뽕" : orderPrice = 8000 * amount; break;
		case "순대국" : orderPrice = 9000 * amount; break;
		}

 

 

 

 

3. 응답 페이지를 생성 후 응답한다.

  • 직접 페이지를 문자열로 생성한 뒤 스트림으로 내보내기를 할 수도 있지만
  • 페이지 작성이 더 쉽고 응답을 보여주는 (역할에 대해 구분하여)
  • 응답만 전용으로 할 수 있는 jsp쪽으로 request에 값을 담은 뒤 forward 해서 화면에 출력해보자.

  • 서블릿을 타긴하지만, 결과값은 다른 JSP 쪽으로 위임하도록 한다. 
  • 위임을 시킬때 필요한 것은 무엇이 있을까? 이 위임된 페이지에서는 이전의 정보가 필요하다.
  • 1. 메뉴 2. 수량 3. 총가격
  • forward라는 기능을 사용 할때는 request 라는 인스턴스에 setAttribute 해줄 수 있다. 

위임전 넘겨줄 정보들

 

  • RequestDispatcher 라는 인스턴스가 필요한데, 이는 request 로부터 가져올 수 있다. 
  • import 해준다. 

 

  • 임포트 후 에러줄이 사라진것을 확인했다면 다음은 path 에 위임될 새로운 jsp 경로를 작성한다. 
  • 아직 만들지 않았으므로 해당 경로에 파일을 만들어 준다.  
  • 항상 RequestDispatcher 에 경로를 적을때는 이미 was의 최상위 root  가 적용된 상태이다. 

  • 서블릿 컨테이너 내부에서는 /root가 적용된 상태이다.
  • root는 web 이라는 폴더를 기준으로 보면된다. web은 톰캣에서 보여주고 있는 프로젝트의 최상위 경로이다. 

 

  • 어떻게 작성을 하던 맨 앞에 슬래쉬(/)를 붙였을때는 절대 경로가 된다. 
  • 슬래쉬를 제거하면 상대경로가 된다. 
  • 슬래쉬를 붙인다면 (/) 이는 root  폴더, 즉 최상위 경로를 의미하게 된다. 

 

  • 값을 설정해준 request를 forward에 보내주면 다시한번 담겨있는 값들이 깊은 복사가 되어 위임처리 된다.  
    RequestDispatcher rd = request.getRequestDispatcher("/jsp/5_response.jsp");
    rd.forward(request, response);

 

  • jsp 페이지 생성

 

 

더보기
package com.greedy.menu.controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/menu/order")
public class MenuOrderServlet extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/* 서블릿에서 하는 일
		 * 1. 요청 받은 값 확인 및 검증
		 * 2. 비지니스 로직 처리
		 * 3. 응답 페이지 생성 후 응답
		 * */
		
		/* 1. 요청에 대한 처리 */
		request.setCharacterEncoding("UTF-8");
		
		String menuName = request.getParameter("menuName");
		int amount = Integer.parseInt(request.getParameter("amount"));
		
		System.out.println("menuName : " + menuName);
		System.out.println("amount : " + amount);
		
		/* 2. 비즈니스 로직 처리 */
		/* 비즈니스 로직은 대부분 DB에 CRUD 연산 등을 이용해 이루어지게 된다.
		 * 여기서는 DB 연결을 할 것은 아니기 때문에 간단한 로직 처리만 해보자.
		 * */
		int orderPrice = 0;
		
		switch(menuName) {
		case "햄버거" : orderPrice = 6000 * amount; break;
		case "짜장면" : orderPrice = 7000 * amount; break;
		case "짬뽕" : orderPrice = 8000 * amount; break;
		case "순대국" : orderPrice = 9000 * amount; break;
		}
		
		/* 3. 응답 페이지를 생성 후 응답한다.
		 * 직접 페이지를 문자열로 생성한 뒤 스트림으로 내보내기를 할 수도 있지만
		 * 페이지 작성이 더 쉽고 응답을 보여주는 역할에 대해 구분하여 응답만 전용으로 할 수 있는 jsp쪽으로
		 * request에 값을 담은 뒤 forward 해서 화면에 출력해보자.
		 * */
		request.setAttribute("menuName", menuName);
		request.setAttribute("amount", amount);
		request.setAttribute("orderPrice", orderPrice);
		
		/* 서블릿 컨테이너 내부에서는 /root가 적용된 상태이다. */
		RequestDispatcher rd = request.getRequestDispatcher("/jsp/5_response.jsp");
		rd.forward(request, response);
		
		
		
		
		
		
		
	}

}

 

 

 

  • 스크립틀릿(<% %>)을 이용해서 값을 꺼내준다. 
  • jsp는 결국 서블릿 화 되기 때문에 위임의 과정역시 서블릿과 동일하다.
  • 앞에 request에 setAttribute로 담은 값을 getAttribute로 꺼내올 수 있다.
  • getAttribute 로 꺼내올 때의 리턴값은 항상 Object 타입이다.
  • 그러므로 필요한 타입으로 다운캐스팅이 필요하다. 

 

 

 

  • 넘어온 값을 화면에 표현해보자
  • 화면에 출력할 수 있는 익스프레션 태그<%= %>를 사용한다. 
	<h3>주문하신 음식 : <%= menuName %></h3>
	<h3>주문하신 수량 : <%= amount %>인분</h3>
	<h3>결제하실 최종 금액 : <%= orderPrice %></h3>

 

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>5_response</title>
</head>
<body>
	<!-- 응답하기 위해 헤더에 text/html; charset=UTF-8 설정을 해주어야 하는데 이미 위에서 페이지 지시자 태그에 작성해 두었다. -->
	<%
		/* request에 setAttribute로 담은 값을 getAttribute로 꺼내올 수 있다. */
		String menuName = (String) request.getAttribute("menuName");
		int amount = (Integer) request.getAttribute("amount");
		int orderPrice = (Integer) request.getAttribute("orderPrice");
	%>
	<h3>주문하신 음식 : <%= menuName %></h3>
	<h3>주문하신 수량 : <%= amount %>인분</h3>
	<h3>결제하실 최종 금액 : <%= orderPrice %></h3>
</body>
</html>

 

 

 

  • 다음과 같이 위임된 화면에 값이 잘 넘어가서 나타나는 것을 확인 할 수 있다. 

 

 

흐름을 정리해보면 다음과 같다. 

 

  • 1. 클라이언트 쪽에서 서버로 요청을 보냄
  • 2. 처음에 서블릿에서 해당요청에 대한 비즈니스적 처리를 함
  • 3. 중간과정인 service 나 dao 처리를 거침
  • 4. db 처리,  수행된 db 처리가 다시 servlet 으로 돌아옴
  • 5. servlet 에서는 직접적인 응답처리보다는 이를 JSP 쪽에 위임 함 (forward, 즉 request 와 response)
  • 6. jsp는 결국 서블릿 화 되어서 구동 됨
  • 7.이 구동이 클라이언트에게 다시 전달 됨 

 

 

 

 

 

 

정리


 

 

 

  • 이제 MVC 패턴으로 다시한번 보자면 서블릿은 Controller 의 역할을 하게 될 것이다. 
  • 즉 사용자의 요청을 받아 검증하거나 확인하고 
  • 비즈니스 로직, 즉 서비스 쪽으로 넘겨서 응답받는 처리이다. 

 

  • jsp는 VIEW 에 해당하는 영역이 될 것이다. 

 

 

  • JSP는 변경되지않으면 컴파일 되지않고, 변경될 시 동적으로 로딩된다. 
  • 즉, JSP을 변경시 따로 서버를 구동시키는 처리, 즉 컴파일을 하지 않아도 된다. 웹 컨테이너가 자동으로 수행한다. 
  • JSP파일은 서블릿처럼 소스 폴더 하위에 놓는것이 아니라, web 하위 디렉토리, 폴더에 작성해야 한다. 

 

 

  • 주석태그 : 클라이언트 쪽에 출력되지 않는다.
  • 지시자 태그 : include, page, taglib 등이 있다. 
  • 선언문 : 필드에 대한 값들을 선언할 수 있다. 
  • 스크립틀릿 : 간단한 코드 작성가능
  • 익스프레션 : 화면상에 값을 나타낼 수 있다. 

 

  • 표현태그에서는 세미콜론(;)을 붙이지 않음에 유의한다.

 

 

  • 표현 태그내에 세미콜론을 붙이면 위 아래 사진이 동일한 의미가 된다. 
  • 스크립틀릿 코드는 코드를 작성하는 부분이기 때문에 세미콜론을 붙여도 문제없다. 

 

 

  • 내장된, 기본적으로 제공하는객체를 내장객체라고한다. 따로 선언할 필요 없다.  

 

 

 

 

  • exception 내장객체도 사용해봤었다. 
  • 다만 이때 지시자 태그 내 isErrorPage가 true 여야한다. 

 

 

 

  • 페이지 안에서 쓸수있는 jsp 영역 < 위임을 함으로서 사용할수 있는 request 영역 < 하나의 고유값을 통해 인식될 수 있는 session 영역 

 

 

  • 지시자 태그 중 import는 두번이상 사용할 수 있다. 다른 지시자 태그는 한군데에 한번만 써야한다. 

 

  • jsp에서 문제가 발생할 수 있는 경우는 다음과 같다. 

 

 

 

1. Translation Time Error : jsp에서 서블릿화 되기 위해서 .java가 되는 기점에 translate 되며 발생하는 오류

   - 문제가 있을경우 자바파일로 해석하지 못한다. 

 

  • 2. java 파일이 class 파일로 컴파일 되는 시점에 발생하는 오류 
  • Servlet init() : 서블릿 초기화가 되어 동작되는 시점

 

 

 

'Programming > Servlet&JSP' 카테고리의 다른 글

EL-JSTL 실습  (0) 2023.04.24
jsp 액션태그  (0) 2023.04.03
JSP 태그 종류와 사용법(1)  (0) 2023.03.29
10. EL & JSTL (2)  (0) 2022.03.08
10. EL & JSTL (1)  (0) 2022.03.08

+ Recent posts