-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathServlet.txt
669 lines (461 loc) · 44.1 KB
/
Servlet.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
<------------------------------ 웹 프로그래밍 개요------------------------------ >
servlet > JSP -> JSP MVC -> Spring MVC
* 클라이언트(요청자) 프로그램 <> 서버 프로그램(제공자)의 동기화가 되어야 CS(Client/Server) 프로그램을 구현할 수 있다.
* 클라이언트를 변경하려면 모든걸 전부 업데이트 해야했음 (설치와 재설치) > 이런 과정에서 다른 프로그램에 영향을 줌
> 설치 재설치에 대한 부담 커짐 > 데이터 전송시 소켓, RPC 등을 이용 > 데이터 전송 구현의 어려움 > 웹(HTTP)이라는걸 이용하기로 함
* 웹 프로그램은 서버 클라이언트 프로그램이 아니라 웹 프로그램에 서버 클라이언트를 얹을 수 있다.
* 정적인 페이지를 전달하는 웹 서버의 환경을 바꿈 > 동적으로 페이지를 (디비연동해서) 만들 수 있는 환경 추가 > 웹 서버(서버 프로그램이 간단하게 얹어질수있는 환경 만듬)
> 웹 클라이언트(사용자 요청, 서버 전달 목록을 볼 수 있는걸 브라우저를 이용함)
* Page > get request > web server
Page < Response < web server
* 위의 구조에서 page가 문서를 요청 > 서버에서 동적으로 만들어지고 사용자 목록을 만들어줌 > page에서 특별하게 클라이언트 프로그램을 만들 필요없음 왜? 문서로 전달되고 그것을 브라우저를 이용하면 되니까 > 결국 클라이언트 프로그램을 재설치 할 필요가 없어짐
>> 이런 방식으로 서버 프로그램과 클라이언트 프로그램이 나뉘어지며 웹개발을 이용한 서버 프로그램을 만들게됨 과거에는 CS 프로그램이라 말했지만
웹으로 넘어오며 클라이언트가 사라짐 >> 클라이언트가 frontend 서버가 backend가 됨
< ------------------------------웹 서버 프로그램과 servlet ------------------------------>
* 과거
페이지 요청 서버에 전달 > 서버에선 만들어진 문서를 반환 > 하지만 회원이라는 목록이 미리 만들어지긴 불가능함 > 따라서 동적으로 목록을 만들기 위한 코드(Server App)가 만들어져 있음 > 코드를 찾았으면 코드를 실행해 디비에서 목록을 만들고 반환함 > 코드를 실행할 수 있는 환경이 필요함
[실행환경(Web Server) + 코드 실행환경(WAS(Web Application Server))]
* 왜 웹 서버 응용 프로그램을 Servlet이라 명칭할까?
페이지에서 사용자 요청 내용(리스트, 가입, 수정, 삭제 등등) > 서버 어플리케이션은 사용자가 어떤 것을 요청하냐에 따라 파편화된 녀석들이 실행되고 프로그램이 끝나버림
> 조각나버린 서버 어플리케이션 SERVer application LET (SERVLET)
<------------------------------ WAS(톰캣) 설치하기 #1 ------------------------------>
JAVA_HOME 에 JDK 경로 패스 지정안하거나
다른 프로그램이 실행하며 톰캣이 이미 사용중이면
위 두가지를 어기면 톰캣 bin 폴더에 startup.bat 파일 실행안됨(계속 안떠있음 화면이)
>> 제대로 설정 하면 > localhost:8080브라우저 창에 검색하면 고양이 보이면 성공~~!
<------------------------------ WAS(톰캣) 설치하기 #2 ------------------------------>
* 톰캣 > WAS (Web Application Server)
> WAS + 자바 어플리케이션을 개발할 때 사용하는 웹 서비스 기능이 포함됨 >> 웹 서버라 볼 수 도있음 (웹문서를 제공해주는 서비스)
* 톰캣을 웹문서를 제공해주는 방식으로 사용하기!
C:\tools\apache-tomcat-9.0.55\webapps\ROOT 에 text파일 아무거나 넣기 (내용추가해서)
> http://자신의컴퓨터아이피:8080/textfile.txt >> 기본은 index.jsp파일임 (방화벽 열고 포트포워딩 완료하면 원격접속 가능)
<------------------------------ WAS(톰캣) 설치하기 #3 - Context 사이트 추가하기------------------------------ >
-context 사이트란
* 각 폴더들을 하나로 만들기 위해 폴더를 나눠 여러 사람이 분업해서 만들기 위해 이용됨
* root/inext.html, 각각의 폴더들 >> 여기서 각각의 폴더들의 볼륨감이 커지면 따로 떼어내서 그 팀에 맡겨놓음 (완전히 별개사이트같이) 다만 서비스 문맥은 유지함
* 서비스 문맥은 유지하되 만드는 디렉토리나 서비스는 별개로 함 (Context 또는 가상경로, 가상사이트)
* 물리적으로는 두 개의 사이트지만 문맥상 합쳐서 root사이트의 디렉토리로 사용되는 하나의 사이트로 보임
-context 사이트를 만드는 방법
아파치 / conf / server.xml 내에
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"> 구문 아래에 적음
<Context path="이름" docBase="전체경로" privileged="true"/> 이걸 적는다.
위 구문의 해석 : 현재 호스트가 있는데 로컬호스트로 서비스가 진행된다. it란 폴더가 없지만
위 디렉토리에 있는 문서(전체경로)를 it라는 가상디렉토리와 연결해 서비스가 될 수있게 해라
< ------------------------------처음으로 서블릿 프로그램 만들기------------------------------ >
* 자바 서버 어플리케이션 수많은 조각중 요구되는 것만 실행됨 (조각나있음)
* public void service 함수를 main 함수로 생각!!
< ------------------------------서블릿 객체 생성과 실행 방법------------------------------ >
* 톰캣에 의해서 서블릿 코드 실행되도록 코드 배치와 요청
- 아파치 폴더의 ROOT/WEB-INF/classed/여기에 서블릿 코드 둠(패키지가 있으면 패키지 깊이대로 경로 가져와야함)
- 클라이언트에서 이 클래스파일들을 요청할수가 없음 왜? web-inf폴더내 자원들은 서버쪽에서만 사용할 수 있음 (비공개영역이라 볼 수 있음)
* 서블릿이 실행되는 시점과 방식
- 사용자가 요청할때 사용되는 이름을 classes내 클래스파일과 매핑해 해당코드를 실행하고 결과를 돌려주는 방식
- 사용자가 원하는 이름을 지정할 수 있음(톰캣내에서) 실제 Nana가 이름이지만 URL(이름)을 매핑지정하면 해당 이름(URL)으로 호출 가능(/hello <예시)
- 매핑은 ROOT/WEB-INF/web.xml파일에 설정함
- 매핑 예시
<!-- 이곳에 mapping 정보 넣는다. -->
<servlet>
<servlet-name>na</servlet-name> <!--실행할 서블릿 코드-->
<servlet-class>Nana</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>na</servlet-name>
<url-pattern>/hello</url-pattern> <!--혹시 /hello라는 url을 보면 위의 Nana서블릿 클래스를 실행해라 : 사용자에게 제공하는 URL-->
</servlet-mapping>
-매핑 설정 완료하면 아파치 툼캣 다시 실행! 왜? web.xml변경되었으므로
-localhost:8080/hello로 접속하면 요청받은 웹서버는 파일을 찾아보고 없으면 WAS에게 넘기고 WAS가 자신의 매핑정보를 뒤지고 hello를 찾아내어 서블릿 코드 실행
-하지만 System.out.println("");은 서버쪽 콘솔에 출력함 (startup.bat)파일 열고 실행하면 hello servlet출력됨
-클라이언트(웹)에 출력할 수 있는 도구는 HttpServletResponse response 객체가 된다.
<------------------------------Servlet 문자열 출력------------------------------>
* 자바 웹 프로그래밍이란
- 자바
- 웹(console, window가 들어갈수도 있음 web이란 > 입출력이 웹으로 바뀌고(UI역할) 웹이라는 것이 서버에서 클라이언트로 원격으로 데이터를 전달되는 형태 또는 클라이언트로부터 원격으로 입력을 받을 수 있음)
> 기존 형태와는 다르게 입력과 출력의 내용이 다르기때문에 먼저 이해하는게 필요함
- 프로그래밍
* service 함수는 HttpServletRequest request, HttpServletResponse response가 있다. request는 클라이언트 > 서버로 요청(입력도구)을 보내는것이고, response는 서버 > 클라이언트로 출력을 담당(출력도구)한다.
>> HttpServletRequest : 사용자가 서버쪽으로 데이터를 요청할 때, 요청에 대한 기능과 속성들을 가지고 있는 객체.
>> HttpServletResponse : 서버에서 만든 데이터를 사용자에게 넘겨줄 때 사용하는 객체 즉 사용자의 요청에 의한 응답(서버 -> 브라우저(사용자))에 대한 객체. 사용자에게 응답해 줄 때 사용
* 출력과 입력의 기본은 Stream이다. > OutputStream os = response.getOutputStream(); 하지만!
* 바이너리, 바이트 값으로 출력할 것이 아니고 문자열 출력이므로 좀 더 편하게 쓸 수 있게하는 PrintStream out = new PrintStream(os, true);
>true값은 네트워크로 출력되는 스트림은 글자하나 쓰고 보내는게아니라 버퍼에 좀 쌓여야(8kb)보내므로
기다리는걸 방지하고자 기다리지 말라는 옵션(flush)
* out.println("Hello Servlet!!"); 하면 원격에 있는 클라리언트(웹)로 출력함
* 그런데!!! 일반적으로 문자열 출력시는 PrintStream이 아니라 PrintWriter out = response.getWriter();를 이용 > 왜? 자바 IO에서는 Stream, Writer계열이 있다. > 다국어 일 경우 Writer를 사용~! 우린 한국어니까 Writer 이용!!
* 이렇게 일일이 코드를 수정하고(텍스트파일로) > 컴파일 다시하고(cmd) > WEB-INF내 classes로 클래스파일 옮기고 > 서버 재실행하고 > 브라우져에 요청함.
>> 복잡하쥬? 이래서 이클립스 쓰는겁니다~!
* 이클립스는 (코드수정 컴파일 배포 톰캣서버재시작 브라우저로 요청) 단계를 Ctrl F11로 끝냄!!!
* 이클립스가 컴파일, 문서관리, 실행까지 담당해줌 >> 이클립스도 이런것들을 관리하기 위한(실행, 편집, 배포) 또다른 파일 필요함!! >> 프로젝트 파일 Dynamic Web project
>> WAS는 톰캣만 있는게 아니라서 실행환경 설정 해줘야함 Target runtime
< ------------------------------이클립스를 이용한 서블릿 프로그래밍------------------------------ >
* JSP project의 홈 디렉토리는 src/main/webapp이다 원래는 WebContent라는 이름이었으나 변경됨
* JSP project의 Java Resource는 src/main/java로 이름이 변경됨
* 이클립스에서 CTRL F11을 누르면 > 문서가 톰캣에게 디렉토리가 옮겨지고 톰캣이 다시 실행되고 이클립스에 브라우저를 띄우고 index.html열림(톰캣에 배포된 디렉토리에 있는 index.html이 열림, 이클립스 파일이 직접열리지 않음)
* 외부 브라우저로 열고 싶으면 window - web browser에서 선택하면 됨
* 문서를 열면 주소가 http://localhost:8080/JSPPrj/index.html이다 여기서 JSPPrj는 context명이다 기본 루트 프로젝트는 context명을 갖는게 좋지 않음
>> 따라서 프로젝트명을 root로 변경 (properties > web project settings > 이름을 /로 변경
>> 이후 톰캣 정지 > 기존 JSPPrj Context명으로 연동된것 지우고 다시 실행 > 이후 브라우져 보면 http://localhost:8080/index.html 이렇게 컨텍스트명 사라짐
* html파일은 이렇게 열리는 구나!! 그럼 자바 파일은??????????
자바파일은 src/main/java에 생성한다
파일 작성후 실행을 하려면 클래스 파일을 web.xml에 URL을 매핑해야함 (< 서블릿 객체 생성과 실행 방법 > 참조)
web-inf내부에 파일이 존재하지 않으므로 기존 톰캣에 있던걸 복붙함!!
<------------------------------Annotation을 이용한 URL매핑 ------------------------------>
* 클래스 상단에 @WebServlet("/hello") 로 annotation을 붙인다.
컴파일 과정에서 사라지지 않는 주석이다.
객체를 사용할 때 객체에 묻어있는 주석정보를 꺼내 활용할 수 있음 (일종의 metadata)
클래스를 나중에 사용할 때 메소드나 클래스에 붙여진 annotation을 보고 실행하는데 영향을 줄 수 있다.
따라서 annotation을 이용해 매핑 정보를 담을수도있다.
* annotation을 이용하기 위해서는 먼저 web.xml설정을 변경해야한다.
metadata-complete 값이 true로 되어 있는데 이것의 의미는 url 매핑을 포함한 메타 데이터 설정이 web.xml에 있다는 뜻입니다.
false는 url 매핑을 포함한 모든 메타 데이터 설정이 다른 파일에도 있다는 뜻입니다.
false로 바꿔줘야 어노테이션으로 url 매핑이 가능합니다.
* annotation이 좋은 이유 : 협업시 web.xml에 서로 매핑하려고 이용할것 > 복잡해짐
annotation을 사용하면 각자 맡은 부분을 분할하기 때문에 깔끔함
<------------------------------ Servlet 출력 형식의 이해(형식을 지정해야 하는 이유) ------------------------------>
for (int i = 0; i < 100; i++) {
out.println((i+1) + ": hello Servlet!!");
}
서블릿 코드에 넣고 실행하면 브라우저에 전달되는것은 웹문서이므로 println의 내려쓰기가 적용되지 않는다. 왜? 클라이언트는 웹문서로 인식하기 때문에
f12로 소스를 보면 소스는 내려쓰기가 되어있지만, 브라우저가 웹문서로 인식하면 내려쓰기가 인식되지 않는다.
>> 브라우저는 HTML(Hyper Text Markup Language)를 통해 내려쓰기를 하므로 <br>태그가 줄바꿈 이다!!
다만 브라우저 별로 println을 줄바꿈으로 인식하고 <br>을 인식못하거나 <br>은 인식하고 println은 인식 못하는 경우가 생김 왜 ?
>> 컨텐츠 형식을 알리지 않아 브라우저가 자의적인 해석을 함
IE 는 html로 해석, chrome은 text로 해석함
<------------------------------ 한글과 콘텐츠 형식 출력하기 ------------------------------>
* 한글 입력시 깨짐 : 서버에서 한글을 지원하지 않는 문자코드로 인코딩하거나, 서버에서 UTF-8로 인코딩해서 보내지만 브라우저가 다른 코드로 잘못 해석한 경우
// TODO JAVADOCS: 클라이언트로 전송되는 응답의 문자 인코딩(MIME 문자 집합)을 설정
response.setCharacterEncoding("UTF-8");
//클라이언트가 받고나서 어떻게 해석할것인가에 대한 정보를 전달
// TODO JAVADOCS: 클라이언트로 전송되는 응답의 콘텐츠 유형을 설정합니다. 주어진 콘텐츠 유형에는 문자 인코딩 사양이 포함될 수 있습니다
response.setContentType("text/html; charset=UTF-8");
>> 이후 개발자모드 네트워크 새로고침이후 확인하면 contenType이 생성된것을 볼 수 있음
<------------------------------ GET 요청과 쿼리스트링(사용자의 요청) ------------------------------>
* 쿼리스트링 : http://locahost/hello?cnt=3 에서 ?cnt=3가 추가되면 이게 쿼리스트링(추가적인 옵션을 질의(햄버거 양파뺴달라, 샷추가와 같은것))
* 브라우저에 쿼리스트링을 넣어 값을 받는것 >> get 요청
* 초기 브라우저를 주소를 가지고 띄우는 작업도 GET요청이 default 이다. >> 출처 https://u-it.tistory.com/entry/GET%EB%B0%A9%EC%8B%9D-POST%EB%B0%A9%EC%8B%9D-input-typehidden
int cnt;
cnt = request.getParameter("cnt"); 으로 이용한다. 이때 입력도구를 이용하므로 사용자가 어떤 옵션을 주는지 받아와서 문자열로 값을 반환한다.
따라서 cnt에 그냥 대입 불가
>> cnt = Integer.parseInt(request.getParameter("cnt"));
>> 다만 cnt의 초기값이 지정되지 않았으므로 브라우저에 쿼리스트링을 입력하지 않으면 500오류 발생
<------------------------------ 쿼리스트링을 위한 기본값 사용하기 ------------------------------>
/hello?cnt=20 >> "20" 전달
/hello?cnt= >> "" 전달
/hello? >> null
/hello >> null
위의 4가지 get 요청이 가능하다.
* 기본값 사용방법
1. int cnt값을 기본값으로 초기화
2. String temp 임시변수에 사용자 요청을 받아옴
3. null체크, 공백체크 후 아니면 Integer변환후 cnt에 대입
위와 같은 과정으로 기본값을 사용할 수 있다.
html과 연동해서 사용가능
index.html
환영합니다.<br>
<a href="hi">인사하기</a>
<a href="hi?cnt=3">인사하기</a>
Nana.java
String cnt_ = request.getParameter("cnt"); // 임시변수 : null, ""값 들어왔을때 오류방지를 위한 임시변수
int cnt = 10;
if (cnt_ != null && !cnt_.equals(""))
cnt = Integer.parseInt(cnt_);
for (int i = 0; i < cnt; i++) {
out.println((i + 1) + ": 안녕 Servlet!!<br>");
}
코드가 위와 같을때
사용자가 a태그 값을 클릭하면 hi브라우저에 cnt값을 get요청으로 스트링쿼리값을 전달해 전달값 만큼 화면에 출력가능하다.
<------------------------------ 사용자 입력을 통한 GET 요청 ------------------------------>
* 사용자에게 몇번 출력할지 값을 입력받을 수 있다.
사용자가 3을 입력후 클릭 > hi?cnt=3 생성 > get요청으로 전달 . . . > hi문서생성 > 클라이언트 화면에 생성
html의 웹사이트에서 서로 상호작용하고 사용자가 웹사이트에 데이터를 전송하는 것을 허용하는 태그 <form> 태그를 이용해야 한다.
(출처 JavaScript docs : https://developer.mozilla.org/ko/docs/Learn/Forms/Your_first_form)
<div>
<!-- 사용자 입력을 위한 태그 -->
<form action="hi"> <!-- action에는 servlet mapping 주소가(annotation확인) 들어감 -->
<div>
<label>"안녕하세요"를 몇 번 듣고 싶으세요?</label>
</div>
<div>
<input type="text" name="cnt" />
<!-- 요청을 전달하는 버튼 -->
<input type="submit" value="출력" />
</div>
</form>
</div>
<실제 동작과정>
브라우저는 form태그에 있는 aciton명을 보고 url을 작성(http://../hello > text타입에 입력된 값이 있고 버튼을 누르면 그 값으로 쿼리스트링 만듬 (http://../hello?cnt=value) > get 요청으로 WAS로 전달되고 여기서 쿼리값을 이용한 브라우저 생성됨
> 클라이언트(웹브라우져)로 반환(출력)
<------------------------------ 입력할 내용이 많은 경우는 POST 요청 ------------------------------>
* get요청시 쿼리스트링을 통해 값을 전달, 서버쪽에선 값을 받아서 서비스 하는데 이용함 >> 입력할 내용이 적은경우
* 입력할 내용이 많은 경우 > POST 요청
문제 1 : 입력할 내용이 URL의 제한 길이를 초과하면 오류 발생 >> 왜? get요청은 url로 나타나기 떄문에 따라서 post 요청 이용
문제 2 : 쿼리스트링이 달라고하는것이 문서라면 문서에 대한 옵션값임 >> 간단한 요청(햄버거에 치즈빼기)정도만 처리하지 장문의 내용은 옳바르지 않다.
* 요청과 제출, 두 단계로 나누어서 일을 처리하려고 할 때의 두 가지 요청
get이라는것을 한번 요청하고 끝나는 경우도 있지만, 입력할양이 많으면 일정양식을 통해서 전달받는경우가 있다.
post는 많이 사용됨 : 어떤 업무적인 일을 처리하기위한 입력양이 많으면 한번에 get요청이 아닌 > get 요청과 post요청을 나눔
> 처음에는 입력 form을 받기위한 get 요청 후 결과를 가지고 post함
ex) 햄버거 주문을 위한 주문서 주세요 -----------------get---------------> 요청처리
<---------------form전달----------- form준비
이런이런 햄버거 주세요(양많음) ----------------post---------------> 요청처리
<---------------결과전달----------- 전달
실제 코드로서 요청
form의 action옵션에 서블릿 매핑주소만 적용하면 >> 디폴트값은 쿼리스트링을 이용한 get 호출 발생
이것을 method="post"옵션을 같이 넣으면 form은 주문대장을 받은것이 되고 버튼을 누르면 제출이 된다고 보면됨 (햄버거 주문대장 > 제출) >> method = "get" or "post"
>> 입력내용 많으므로 URL이 아닌 <요청 body(?)>에 붙어서 전달됨
>> 전달되고나면 url은 /servlet_mappingName으로만 보여짐
>> 그러면 어디로 요청할때 사용된 데이터를 받았을까? f12눌러서 network의 playload보면 URL이 아닌 이곳에 keyword=value&keyword2=value2 전달된것을 볼 수 있다.
>> 따라서 크기 제한 없음!
>> 문제! post시 한글이 깨짐.. 왜? 출력이깨진걸까 입력이 깨진걸까?
>> F12의 networo-playload에선 잘 전달 됐으나 서버에 갔다가 문서에 실어나온 내용은 깨짐
>> 서버쪽에서 문서를 만들다가 깨졌나?, 내가 전달한 내용이 한글이 깨져서 전달된건가? >> 잘 모름
>> 글자를 적는 폼(form)은 UTF-8 설정 적어서 post방식으로 전달함 --------> 웹 서버 (하지만 톰캣의 기본 인코딩방식은 ISO-8859-1로 읽음) ---> 한글깨짐 --> 깨진걸 다시 출력 ---> 한글 깨진화면 보여짐
>> 해결책!
>> 입력도구에 request.setCharacterEncoding("UTF-8");로 설정을 하고 읽음 톰캣서버 설정을 바꿀수도있지만 잘 안건드림(톰캣은 여러 사이트를 가질 수 있으므로)
<------------------------------ 서블릿 필터(Servlet Filter) ------------------------------>
웹 서버 <---> WAS---------request--------> Servlet Container(Servlet실행시킨걸 담아놓음)
<---------response--------
톰캣(WAS)에서 사용자로부터 요청이 들어오면 적절한 소프트웨어 실행(실행요청:request): Servlet
>> 서블릿 실행시 메모리상에 존재하게 되는데 메모리상에 존재할때 당시의 공간을 Servlet Container라함
>> WAS는 Servlet을 실행시켜서 Servlet Container에 담아놓고 결과를 돌려줌(response) 이후 담아놓은 Servlet이 더 이상 사용되지 않으면 삭제됨
서블릿말고도 만들수있는 또다른 오브젝트가 있다!! >> FILTER
영어가아닌 한국어를 쓰니 계속 setCharacterEncoding을 바꿔주어야함 >> 그럼 톰캣설정자체를 바꾸면? (여러 애플리케이션 서비스를 담당하므로 좀 부담스러움)
>> 이런 수고를 덜어주는것이 필터다!!
필터의 역할 : 요청이 들어오면 중간에 가로채서 필터가 먼저 실행되고 Servlet Cotainer가 실행됨
그리고 필터는 서블릿 컨테이너를 바로 실행할것인지 말것인지도 결정할 수 있다. (인증과 권한시 권한 부여할지말지) like 수문장
즉 모든 서블릿이 가져야할 기본 설정을 이곳에 설정하면 전체적용가능
그리고 서블릿 컨테이너실행후 다시한번 실행되므로 사전 사후 작업 때 이용할 수 있다.
(톰캣이 처음실행될때도 한번실행됨 초기에는 2번 실행되고 이후 요청마다 1번씩 실행됨)
* 필터만들기
1. 필터클래스 생성 (javax.servlet.Filter 인터페이스를 구현함)
2. web.xml에 필터 생성 (서블릿 매핑과 동일 단지 java파일에서 annotation은 따로 지정하지 않는다.)
<filter>
<filter-name>characterEncodingFilter</filter-name> <!--실행할 필터 클래스 이름 -->
<filter-class>com.hoseok.web.filter.CharacterEncodingFilter</filter-class> <!-- 실제 필터 경로 -->
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name> <!--실행할 필터이름 -->
<url-pattern>/*</url-pattern> <!-- /*의미는 모든 URL에 대해서 필터 클래스를 실행하라-->
</filter-mapping>
3. 필터클래스에서 원하는 형식 구현
> 우리는 인코딩 설정을 필터를 이용해 하기 위함이므로 request.setCharacterEncoding("utf-8");
> 사전 작업이므로 인코딩 설정 이후 chain.doFilter(request, response);로 서블릿 컨테이너 정상실행하게 함
* annotation을 이용한 필터만들기
1. 위의 web.xml설정 없이 그냥 필터클래스에 @WebFilter("필터적용경로"); 설정하면 됨 >> 얼마나 편한~~!가!!
* FilterChain chain의 역할
chain.doFilter() 메소드를 실행하지 않으면 우리가 호출하려는 서블릿이 실행되지 않는다.
왜? 필터에서 서블릿을 실행할지 말지(전이시키는) 역할은 chain이 하고 있다.
chain.doFilter()가 html에서 매핑된 서블릿을 실행시키게 흐름을 넘겨주는 역할이다.
>> 따라서 chain.doFilter() 전에 코드를 작성하면 before Filter
>> 후에 chain.doFilter()를 작성하면 agterFilter가 된다.
>> 우리가 하려는 인코딩은 서블릿이 실행되기전 설정되어야 하므로 before Filter로 작성해야한다.
<------------------------------ 학습과제(사용자 입력을 통한 계산 요청) ------------------------------>
* get 요청으로 form을 화면에 띄워줌
* 사용자가 x 값, y값을 입력하고 버튼을 누르면 결과값을 서블릿에서 계산하고
* 최종화면에 값이 얼마라고 띄워주는 프로그램
<------------------------------ 여러 개의 Submit 버튼 사용하기 ------------------------------>
* 기존 덧셈 문제를 개발자 도구로 확인하면 x,y값만 전달됨 >> 왜? 여기 태그에만 name옵션이 있으므로
* 따라서 버튼에도 name옵션을 주면 값을 전달해서 개발자도구에서 확인가능 >> 즉 name이 있기때문에 전달됨
* 다만 버튼 2개에 모두 name이 있어도 둘 다 전달되는것이 아닌 선택된 놈만 전달된다. (operator)
Form Data
x: 3
y: 2
operator: 뺄셈
<------------------------------ 입력 데이터 배열로 받기 ------------------------------>
* 입력받는 input박스가 고정되어 있지 않고 동적으로 변해서 name값을 줄 수 없는 경우도 있다. (이력서내 학력사항 추가 시)
* 만약 동적으로 생성될때마다 이름이 다르면 받는 입장도 처리하기 어려움
* 가변적 길이, 여러 개수의 데이터를 전송해야 하는경우 ----> 같은이름으로 보냄
* 같은이름으로 맞춰주면 ? >> 배열로 전송됨!
* 서블릿에서 받을떄 getParameter가 아닌 getParameterValues()사용
* 개발자 도구 확인하면 num=1&num=2&num=3&num=4 형식으로 전송됨
<------------------------------ 상태 유지를 필요로 하는 경우와 구현의 어려움 ------------------------------>
* 상태 유지를 위한 5가지 방법
1. application
2. session
3. cookie
4. hidden input
5. querystring
* 일단 1~3만 알아보자
<------------------------------ 1. Application 객체와 그것을 사용한 상태 값 저장 ------------------------------>
* 상태를 저장하기 위해 또는 앞에서 다루었던 결과를 잠깐 저장하기 위해 사용하는 객체
* Application 저장소 : 서블릿 컨텍스트(서블릿을 사용할때 그들간 데이터를 이어갈 수 있는 저장소, 서블릿들이 서로간에 문맥을 이어갈 수 있게 저장소로 활용할 수 있다.)
혹은 A라는 서블릿이 결과물을 저장하면, B라는 서블릿이 결과물을 이어서 만들 수 있는 상태공간 즉 서블릿 끼리 자원을 공유할 수 있는 저장소
* Application객체가 Application 저장소(서블릿 컨텍스트를 다룸)
< 대표 메소드 >
* void setAttribute(String name,
Object object) >> 서블릿 컨텍스트에 이름과 오브젝트 타입으로 저장
* Object getAttribute(String name) >> 서블릿 컨텍스트에서 이름값을 입력하면 오브젝트 타입으로 반환됨 (캐스팅해 사용)
<------------------------------ 2. Session 객체로 상태 값 저장하기(그리고 Application 객체와의 차이점)------------------------------>
Application과 Session은 사용하는 메소드와 메소드의 역할이 동일하다 > 그럼 선택적으로 아무거나 써도 되나?
NOPE
이름에서 알 수 있듯이
Application 객체 : 객체를 사용할 때 Application 전역에서 사용할 수 있다. >> 모든 서블릿이 전역적으로 쓸 수 있는 전역 공간
Session 객체 : Session 범주내에서 사용할 수 있다. >> Session의 의미? > 현재 접속한 사용자의 의미 (즉 사용자 별로 공간이 달라질 수 있다.)
테스트해보기 : 웹서버는 브라우저가 다르면 다른 사용자로 인식함(세션이 다르다) > 크롬과 엣지로 실험!
>> 실제로 서로 다른 세션으로 인식함
테스트해보기2 : 그러면 같은 브라우져로 하면? >> 크롬 크롬
>> 같은 세션으로 인식함 왜? > 구글 크롬은 창을 여러개띄우면 여러 프로세스가 아니라 하나의 프로세스에서 흐름을 나눠 갖는 스레드로 나뉜다 따라서 같은 세션으로 인식
> 따라서 웹서버 요청시 같은 프로세스가 요청하는걸로 인식
>> 그러면 서버쪽에서는 사용자를 어떻게 구분하지?
<------------------------------ 웹 서버가 현재사용자(Session)을 구분하는 방식 ------------------------------>
> 어떻게 웹 서버는 사용자(세션)을 식별하고 구분할까?
* 브라우저를 이용해 서버에 무엇인가를 요청 > 서버에서 처리하다가 다른 서블릿에게 전달하거나 공유하고 싶은 내용이 있으면 Application 공간에 저장
> application공간에 사용자마다 저장할 수 있는 세션 공간이 따로 있다.(사물함느낌)
* 따라서 사용자 요청 오면 > servlet실행 > 서블릿 실행요청이 처음 왔을경우 사용자는 새로운 사용자가 됨 > 따라서 서버쪽에는 사용자를 위한 세션이 존재하지 않음
> 새로운 사용자 혹은 기존 사용자든 요청에 의해 실행되는 Servlet이 실행될때 application공간은 모든 요청에 의한 서블릿이 application공간에 값을 남길 수 있음
> 하지만 사용자가 ID를 가지고 있어야하고 Session ID를 가지고 있는 사람많이 세션공간에 값을 넣을 수 있다.
> 그래서 사용자가 처음에 오면 > 세션공간은 못쓰고 application공간만 쓸 수 있다.
> 한번 실행하고 돌아갈때 서버쪽에서 ID 하나를 부여해줌(ex 107번) > 세션공간에 107번 해당되는 공간 만들어짐(107번 사용자를 위한)
> 다시 107사용자가 요청을하면 107식별자를 가져옴 > 이젠 세션에 값을 넣을 수 있다.
> 따라서 브라우저가 달라지면 SID가 없는채로 요청하는 것이고 > 돌아갈때 SID를 새로 부여받으므로 서로 다른 브라우저는 다른 SID이므로 웹서버는 다른 사람으로 인식 따라서 세션이 구분된다.
* SID 확인하는 방법
개발자모드에서 network-header에 쿠키에 SID가 있다. (같은 크롬브라우저를 띄우면 세션아이디가 동일함을 확인할 수 있다.)
* 브라우저 사용없이 툴로 누군가의 세션아이디를 복사해서 가져오면 > 웹서버는 같은 사용자로 인식함 > 해킹위험
* 사용자가 한번 사용하고 계속 사용안하면 웹서버는 세션공간을 계속 유지하나?
> 세션은 이 이유로 추가적인 기능이 있음
> 저장소를 비울때 사용되는 메소드 : void invalidate();
> invalidate처럼 타임아웃을 사용하기도함 : void setMaxInactiveInterval(int interval) : 만약 타임아웃 시간이 1분이고 같은사용자가 1분전에 또 요청하면
> 새로 1분으로 갱신되지만 1분이 지나고나면 새로운 사용자로 인식함(메모리에서 세션공간 수거해감)
> boolean isNew() 세션이 새로 생성 되었는지 확인
> Long getCreationTime() : 세션이 시작된 시간 반환, 1970/1/1시작으로 하는 밀리초
> long getLastAccessedTime() : 마지막 요청 시간, 1970/1/1시작으로 하는 밀리초
> 개별적으로 세션시간을 변경할 수 있음(톰캣은 30분정도?)
<------------------------------ 3. cookie를 이용해 상태값 유지하기 ------------------------------>
* 피트니스 센터엔 자기만의 사물함이 있고 누구나 둘 수 있는 공간이 있음
> 샴푸를 두고 다니는사람, 들고다니는 사람이있음
> 이렇게 가지고 다니는값 == cookie
* Cookie
> 상태값을 서버가 아닌 클라이언트가 가지고 있다.
> 상태값을 클라이언트만 들고있는게 아니라 클라이언트가 가지고 있기도 서버가 가지고 있기도함 > 왔다갔다함
> 왔다갔다해도 상태개 유지된다. (세션에서 꺼내쓸수있지만 가져온 값을 써서 상태를 유지)
* 클라이언트가 서버에 뭔가를 요청할때 값을 요청할 수 있다. (3가지의 값)
1. Header설정 (getHeader())
2. 내가 보내는 데이터(사용자데이터: getParameter())
3. Cookie (getCookies()) <> 반대로 addCookie()로 클라이언트로 보낼수도있다. >> 즉 쿠키를 꺼내쓸 수 있고, 추가할수도있다.
* 서버쪽 상태를 저장해야하는데 어플리케이션이나 세션이 싫다? > 쿠키
>쿠키 저장하기 (클라이언트로 보내야겠다)
> Cookie cookie = new Cookie("c", String.valueOf(result)); // 쿠키를 만들고 "c"라는 키로서 result벨류값을 지정(다만 쿠키값으로 보내는 value는 반드시 URL로 사용할 수 있는 형태의 문자열 이어야한다)
> response.addCookie(cookie); // 지정한 쿠키를 클라이언트로 보낸다
> 쿠키 읽기
> Cookie[] cookies = request.getCookies(); // 쿠키가 여러개 심어질수있으므로 배열로 오고, Map컬렉션처럼 키와 값으로 여러개의 키가온다.
> String c_ = "";
> if (cookies != null)
for (Cookie cookie : cookies) // for문 돌리며 내가 원하는 키값과 쿠키의 키값을 비교하며 찾아야함
if ("c".equals(cookie.getName()))
c_ = cookie.getValue(); // 키값에 해당되는 벨류값 저장
* 3가지 저장소(Cookie, Application, Session) 이 있고 이 각각의 특징, 장단점을 알아야 적절하게 서버, 클라이언트중 어느곳에 둘 것인지 결정가능
<------------------------------ Cookie의 path 옵션 ------------------------------>
* 쿠키를 설정할 때 URL 옵션을 설정할 수 있음 (이 쿠키는 나한테 올때만 가져와라, 나 말고 이 범주안에 있는 서블릿에게만 가져와라) > 쿠키가 비효율적으로 여러가지 값을 전달한다거나, 이름값이 충돌하는걸 막을 수 있음
* 쿠키를 사용할때 URL에 관련된 서블릿에게만 값이 전달되도록 해보자!
> 쿠키 생성 이후에 아래 코드 작성해서 path 등록 (/는 어떤 서비스를 요청하든 value, op값이 전달됨, /notice/면 notice path가 포함된 하위 url을 요청할경우에 value, op값을 서버로 전달한다.)
valueCookie.setPath("/");
opCookie.setPath("/");
* 실제로 전달 되는지 확인하기
> 3 + 누르고 개발자모드 response header Set-Cookie: value=5; Path=/로 설정된걸 확인
> 5 = 누르고 개발자모드 request header에 Cookie: op=+; value=5 확인 가능
> 즉 쿠키가 서버와 클라이언트 사이에서 주고받는걸 알 수 있다.
* Response Header(서버의 응답)에서의 쿠키 의미 : HTTP 요청 수신시 서버는 응답과 함께 set-cookie를 클라이언트로 전송하고 클라이언트는 이 쿠키값을 저장함
* Request Header에서의 쿠키 의미 : 서버로 전송되는 모든 요청과 함께, 브라우저는 Cookie 헤더를 사용하여 서버로 이전에 저장했던 모든 쿠키들을 회신할 것입니다.
<------------------------------ Cookie의 maxAge 옵션 ------------------------------>
* 브라우저가 닫히면? >> maxAge값을 설정하지 않았으면 브라우저의 생존주기와 쿠키의 생존주기가 같아짐
* 쿠키에 maxAge 옵션을 넣으면 가장 큰 장점인 브라우저가 닫혀도 내가 원하는 기간을 설정한 만큼 값을 계속 유지함
* 어떻게 유지하나?
> 브라우저메모리에 있다가 maxAge 옵션을 설정하면 기간동안 브라우저 상관없이 저장되어야 하기때문에
> 외부파일로 쿠키파일이 만들어지고 저장된다.
* Cookie.setMaxAge(int Second); >> 브라우저가 닫혀도 살아있는 생명주기를 설정함 (초단위)
> 실제로 브라우저 세팅에서 쿠키 만료기간을 보면 설정한만큼 남아있는걸 확인할 수 있음
<------------------------------ Application/ Session/ Cookie의 차이점 정리 1 ------------------------------>
* Application에 값을 저장하겠다의 의미 : 모두가 사용할 수 있는 공간에 두겠다.
> 사용범위 : 전역 범위에서 사용하는 저장 공간
> 생명주기 : WAS가 시작해서 종료할 때 까지
> 저장위치 : WAS 서버의 메모리
* Session에 값을 저장하겠다는 의미 : 특정 사용자만 사용할 수 있는 공간에 두겠다.
> 사용범위 : 세션(특정사용자) 범위에서 사용하는 저장 공간
> 생명주기 : 세션이 시작해서 종료할 때 까지
> 저장위치 : WAS 서버의 메모리
* Cookie에 값을 저장하겠다는 의미 : 자원을 웹브라우저에 쓰기때문에 서버자원을 빌리지 않아 부담을 줄임
> 사용범위 : Web Browser 별 지정한 path 범주 공간
> 생명주기 : Browser에 전달한 시간부터 만료시간까지
> 저장위치 : Web Browser의 메모리 또는 파일
사용구분 예시)
1. 데이터를 1년정도 유지해야한다?
> 쿠키가 적합함
(세션은 1년을 유지하기에 힘듬 왜? 1년동안 WAS 서버 메모리 자원을 묶어두고 있으므로 공간낭비 심하고 사용자가 껐다가 키면 Session ID가 바뀌므로 기존세션은 쓰레기값으로 오래 남아있음)
2. notice관련 데이터를 가지고 있고 notice외에는 데이터를 쓸만한 서블릿이 없을경우 그 데이터를 어디에 저장할까?
> 쿠키 적합 (특정 URL에서만 쓸수있게 범위 지정 가능하므로) > 세션이나 어플리케이션은 언제쓸지도 모르는데 서버자원을 사용하믈 부담 증가
<------------------------------ 서버에서 페이지 전환해주기(redirection) ------------------------------>
* 지금까지 계산기 프로그램에선 post이후 서버에서 백지를 돌려받음. 따라서 뒤로가기 하고 다시 요청함
* 서블릿쪽에서 백지말고 사용자가 요구하는 페이지를 돌려주는 방법 >> Redirection
* 현재 계산기에서 값을 저장하는 로직에선 반환하는 것이 아무것도 없음 > 백지
<------------------------------ 동적인 페이지(서버 페이지)의 필요성 ------------------------------>
* 계산기에선 사용자가 누르는 버튼이 실시간으로 누적되고 화면에 보여짐 >> 사용자가 입력한 내용을 서버에서 페이지를 만들 때 끼워 넣어야함.
* 지금까지 만든 방식은 기존 화면을 리다이렉트해 초기 페이지를 반환함 >> 하지만 우리는 눌렀던 숫자들이 보여야함 >> 출력내용이 단순하게 html문서가 아니라 서블릿으로 동적으로 만드는방식 필요함
* 기존 방식
> (calc.html에서)사용자 요청 ----> Servlet(/calc4) --------> redirect(calc.html)
* 만들려는 방식
> (calc.html에서)사용자 요청 ----> Servlet(/calc4) --------> Servlet(/calcpage)(단순 반환이 아닌 동적 생성) : 요청이 오면 출력할 문서를 만듦
* 동적인 문서
> 요청이 있을때 만들어지는 문서(서블릿 프로그램으로 문서출력하면 html이지만 원래있던 것이 아니라 요청을 통해 만들어지므로 "동적인 문서" 혹은 위치상 서버에 있어서 "서버문서" 라고도 한다)
* 따라서 calc2.html의 반환이 아닌 동적인 문서를 반환하자!
<------------------------------ 처음이자 마지막으로 동적인 페이지 서블릿으로 직접 만들기, 계산기 서블릿 완성하기 ------------------------------>
이어서 만들자!
* 서블릿으로 동적페이지 만들기 위해 새로운 서블릿 필요
> service() 함수에 동적인 페이지 만드는 코드 넣음
> doGet(), doPost() >> GET 방식, POST방식을 별도로 처리할 수 있는 메소드 있고 그러면서 두개로 나누어진 서블릿을 합침
* 차이점
> html은 3+4를 입력하면 그대로 3+4가 출력하지만 서블릿은 7이라는 결과가 출력됨
* 만들고자 하는것
> 실행후 버튼을 눌러 post하게 되면 서블릿에선 쿠키로 저장을하고 redirection하면 저장된 정보를 출력함(계산기 출력부분에)
* REQUEST HEADER : 브라우저야 다음번에 전송할때 이 쿠키값을 보내랑
* RESPONSE HEADER : 브라우저야 이 쿠키값을 저장해랑~!
* 자바스크립트를 실행할 수 있는 엔진 객체
// 자바스크립트를 실행할 수 있는 엔진객체
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
try {
exp = String.valueOf(engine.eval(exp)); >> exp는 연산자와 숫자가 혼합된 표현식 이걸 String형식으로 받으면 자바스크립트는 연산을해서 돌려준다.
} catch (ScriptException e) {
e.printStackTrace();
}
<------------------------------ 쿠키 삭제하기 ------------------------------>
Cookie.setMaxAge(int second);
Sets the maximum age in seconds for this Cookie.
A positive value indicates that the cookie will expire after that many seconds have passed. Note that the value is the maximum age when the cookie will expire, not the cookie's current age.
A negative value means that the cookie is not stored persistently and will be deleted when the Web browser exits. A zero value causes the cookie to be deleted.
쿠키의 최대 사용 기간(초)을 설정합니다.
양수 값은 쿠키가 만료되는 시간이 몇 초임을 나타냅니다. 값은 쿠키의 현재 보존 기간이 아니라 쿠키가 만료되는 최대 보관 기간입니다.
음수 값은 쿠키가 영구적으로 저장되지 않으며 웹 브라우저가 종료될 때 삭제됩니다. 값이 0이면 쿠키가 삭제됩니다.
<------------------------------ GET과 POST에 특화된 서비스 함수 ------------------------------>
404 : URL을 못찾음
405 : URL은 있지만 URL을 찾을 수 있는 로직이 없다.
* GET 요청과 POST 요청을 구분해서 작성하기
* 1. service함수에서 구분짓는 방법과
* 2. get요청과 post 요청에 특화된 메소드
* 1. service함수에서 구분짓는 방법
> 메소드의 요청이 get이냐 post냐 반환해줌
> 하지만 html의 method 옵션값이 전달되어 넘어오는게 아니므로 대문자로 사용
> if (request.getMethod().equals("GET")) {
> System.out.println("GET 요청이 왔습니다.");
> }
> else if (request.getMethod().equals("POST")) {
> System.out.println("POST 요청이 왔습니다.");
> }
* 2. 또 다른 방법 get요청과 post 요청에 특화된 메소드
> 부모가 가진 service함수는 get인지 post인지 확인해서 doGet(), doPost() 메소드를 구분해 실행함
> 다만 HttpServlet은 추상클래스이므로 메소드들을 오버라이드 해줘야함
>super.service(request, response);
>> 따라서 공통적으로 작성할것이 없으면 service함수를 오버라이드 하지 않고 doGet(), doPost()만 오버라이딩해 사용
>> 공통적으로 처리할 로직이 있으면 service 함수 구현해서 사용하면 됨
<------------------------------ 계산기 프로그램 하나의 서블릿으로 합치기 calculator.java ------------------------------>
* 이전에 만든 계산기 프로그램
1. 처음에 get요청을해 화면에 출력 ----> get 요청 (GET:/calcpage)
2. 버튼을 누름 ---> post 요청 (POST:/calc3)
> 지금까지는 따로만듦 >> 하지만 doGet(), doPost()를 이용해 하나로 합칠 수 있다.
> 두개로 만들면 문제가 있음 >> 쿠키의 문제
* 쿠키의 문제
두개로 만들게되면 path를 등록하는 setPath()함수는 URL을 하나만 등록 가능하므로 두 개로 나뉜 서비스를 모두 담을 수 없다 >> 따라서 하나로 합치는것이 유리
* 합칠때 이점
form 태그 action옵션 넣지 않아도됨 > 같은 페이지에있는 post를 요청하므로 안넣어도 okay
* HttpServletResponse.sendRedirect("URL"); 의 변화
자기 자신의 페이지로 리다이렉트 (자기가 자신을 호출해도 get요청을 의미한다 >> doGet()함수 호출됨 >> 다시 위의 html 출력)
response.sendRedirect("calculaotor");
* setPath("/calculator") >> 절대로 다른 URL에는 쿠키가 전달될 필요가 없기 때문에 범위를 축소시켜서 쓰는 서비스에게만 전달
서블릿 일단 완료