-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathJSP.txt
1262 lines (738 loc) · 68.8 KB
/
JSP.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
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
< ------------------------------------------------------------JSP 시작하기 (Jasper란 알바생)------------------------------------------------------------>
* JSP 는 왜 쓸까?
프로그램을 만들게되면 사용자에게 응답을 하며 결과물 돌려줌 > 웹문서
간단하면 좋지만 보통 복잡한 형태의 html 구문을 이용해 결과물을 브라우저에게 보내줌
서블릿에선 write함수로 한줄 한줄 한줄 다 수정해줘야함 >> 매우 노가다성
* Jasper의 역할
out.write()로 감싸던 노가다성 서블릿코드를 간단히 바꿀수있게 해줌
.jsp확장자를 붙이면 html내용들을 서블릿 출력 가능 형태로 바꿔줌
* 그럼 언제 출력할수있는 서블릿 코드 형태로 바꿔줄까?
jsp가 요구될때(사용자가 jsp페이지를 요구할때) 바뀜
따라서 URL매핑은 파일명 그대로 URL매핑됨 따라서 대문자보단 소문자가 좋음 (서블릿은 저장되어있는 파일명과 URL이 다르지만 JSP는 그렇지 않음)
jasper가 add.jsp로 만드는 서블릿코드는 톰캣내부 경로에 add_jsp.java파일로 만든다.
* 서블릿과 다르게 jsp는 요청할 때마다 달라진게 있으면 알아서 다시 만들기 때문에 다시 서버를 시작할 필요가 없다.
그리고 우리가 브라우저에서 보는 문서는 이클립스 파일이 아니라 톰캣의 홈 디렉토리로 배포해 브라우저에 띄운다.
다만 실제 톰캣의 워크디렉토리가 아닌 이클립스가 관리하는 별도의 운영을 위한 사본을 만듦 (.metadata)
* 만약 jsp내부에 변수를 선언하고 싶었다
int x = 3;
int y = 4;
>> jsp는 무조건 writer()로 붙여버린다.
>> 코드 그대로 삽입하길 원하면 코드블럭을 붙여야함 <% 이 사이에 자바 코드 넣는다 %> >> 서블릿을 jasper를 통해 만드는 방법
< ------------------------------------------------------------ JSP의 코드 블록 ------------------------------------------------------------>
* 출력문장 사이에 자바코드를 꽂아 넣는 몇가지 방법
* jsp 는 출력되어야 할 문장을 public void _jspService()함수에 출력할수있게 변환하고 쓰여짐 (지역변수 알고리즘) out.writer()로 감싸서
* 코드를 넣고 싶으면 <%%> 감싼다 >> 이것도 _jspService()함수에 들어감 >> 자바코드가 들어감
* y의 값은 : y에서 y의 값을 넣고 싶으면 ---> y의 값은 : <% out.print(y) %> --- 변환----> out.write("y의 값은 : "); out.print(y);
>> 문서내에서 특정 변수를 출력하고 싶으면 이를위한 코드블럭 존재
>> <%=y %> == out.print()와 같은 역할
(팁: write()는 문자열을 출력하기 위한 전문 함수, print()는 다양한 형태의 자료형을 출력할 수 있게하는 수많은 오버로드를 가진 메소드이다.)
* public int sum(int a, int b) { return a + b };
> 이렇게 멤버메소드를 정의하고 싶을때는 <%%>을하면 _jspService()함수에 들어감 > 메소드안에 메소드를 정의 (자바에서는 불가능 > 구문 에러)
> 원래 넣고싶은 위치는 public final class add_jsp {} 클래스에 넣고 싶음
>> <%! public int sum(int a, int b) { return a + b }; %> 이렇게 느낌표를 붙이면 멤버변수로 들어감
* page 지시자 (초기설정을 위한)
<%@ page language="java" contentType="text/html: charset=UTF-8" pageEncoding="UTF-8" %>
서블릿서 UTF-8로 출력하겠다 설정, 브라우저에게 알려주기 위한 내용을 직접 썼었음 (setCharacterEncoding(), setContentType())
기존 함수를 코드블록 <%%>로 덮어서 사용하면 >> 이미출력이 지정된 뒤에 설정하고 있다라는 오류가 발생됨 >> 이런 오류로 코드블록으로 해결이 안됨
>> 지시자를 통해 쓰면 어떤 출력을 진행하는 코드보다 앞서서 설정이 진행되므로 인코딩, 컨텐트타입 설정시 지시자를 사용해야함
* 코드블록의 종류 정리
1. <% %> : 자바 코드를 이용하기 위해 (write로 덮어지는걸 막음), jsp서비스 함수에 옮겨짐
2. <%=변수 %> : 문서내 특정 변수를 출력하고 싶을떄 사용 <% out.print(변수명); %>의 수고를 덜어줌
3. <%! %> : _jspService()함수가 아닌 멤버 메소드로서 선언하고 싶을때 사용됨 add_jsp 클래스에 삽입됨
4. <%@ $> : 페이지 지시자, 어떤 출력코드보다 먼저 인코딩을 설정하거나 컨텐트 타입 설정시 지시자를 이용해야함
< ------------------------------------------------------------ JSP의 내장객체 간단히 알아보기 ------------------------------------------------------------>
* jsp 페이지에서 int page 변수를 선언하면 : 로컬변수가 중복되었다 오류 발생
> 코드블럭 외에 코드가 있다는걸 간과하면 안됨 jasper가 만든 서블릿은 내가 모르는 변수가 있을수 있음
> jasper는 서비스 함수를 만들며 자신이 필요한 변수를 만들어놓음 : 그중 하나가 page
> Jasper에 의해 마련된 변수들은 하나같이 객체를 가리키는 객체형식임 : 이런 변수들을 내장 객체라고 한다(Built in 객체)
> 이런 변수가 있다는것을 알고 있어야하고, jsp에서 코드블럭만들때 내장객체들을 적절히 활용해서 코드를 만들 수 있어야함
* 내장객체 종류
입출력도구: response, request 객체
상태저장도구: applicaiton, session 객체
페이지 내에서 세션, 어플리케이션처럼 임시로 데이터를 저장할수있는 도구 : pageContext (setAttribute, getAttribute가짐)
>> servletContext는 전역적으로 사용한다면 pageContext는 _jspService내부에서만 쓰는 저장소
config : servletConfig
out : 출력도구
page : page의 객체를 참조하는 Object형 페이지 변수
* 내장객체란 jasper가 만들어낸 서블릿 안에있는 미리 선언된 변수, 그것을 이용해 코드블럭에서 사용가능
< ------------------------------------------------------------ JSP로 만드는 Hello 서블릿 ------------------------------------------------------------>
nana.jsp 및 nana.java 참조
< ------------------------------------------------------------ 스파게티 코드를 만드는 JSP ------------------------------------------------------------>
* 순서가 맞으면 여러 코드블록으로 나눠도 되고 합쳐도됨 ----> 이렇게 나뉘면 자바코드만 모아서 보기 어려움 -----> 어떤 블럭이 어디서 오류가 나고 어떤 역할을 하는지 찾기 쉽지 않음----> 스파게티 코드
< ------------------------------------------------------------ JSP MVC model1 ------------------------------------------------------------>
* JSP를 잘못만들면 --> 코드블럭이 복잡해짐 --> 어떻게 쉽게 만들어야 할까? --> MVC model1 --코드는 가능하면 위에 모아놓고 밑에는 출력코드만 모아놓음
* 따라서 입력코드블럭에서 만드는 결과물을 출력코드블록으로 이어주는 변수가 필요함 --> 이런 변수가 MODEL임 --> 블럭을 한곳에 모아놓는 방법은? ---> 출력코드는 <%=변수%>만 두고 나머지 제어블록들은 모두 입력코드로 보냄
--> 출력할 데이터를 미리 만들고 출력코드에서 출력만 하면 코드가 깔끔해짐 --> 제어라는것을해서 출력을 위해 만들어낸 데이터 == model이라고 부름
* 입력과 제어를 담당 (출력할 데이터를 만들어내는 과정)
> Controller (자바코드)
* 출력데이터를 가져다 쓰는 부분 (출력부분)
> View (html코드)
* controller(입력과 제어를 담당하는곳)에서 만든 결과물 == 출력할 데이터
> Model
>> MVC 모델 (자바코드와 출력코드를 최대한 분리시키자)
< ------------------------------------------------------------ JSP MVC model1을 model2 방식으로 ------------------------------------------------------------>
* Model1 : 컨트롤러와 뷰가 물리적으로 분리되지 않은 방식
* Model2 : Controller와 뷰를 물리적으로 분리 (View만 사용자 요청시 서블릿으로 만들어짐, Controller는 미리 컴파일해놓고 있기 때문에 실행속도 개선) >> 따로 유지관리 좋음
* 분리된 컨트롤러와 뷰를 연결하기 위해 Forwarding이 사용됨 (컨트롤러에서 뷰로 이전되며 흐름을 이어받아 코드 진행하는 것 == Forwarding)
> Forwarding은 Dispatcher를 이용해 포워딩을 함
> Controller와 View가 계속적으로 추가되면 Controller는 계속 dispatcher를 가지고 있다.(공통적인사항이 각자 가지며 늘어남)
* Dispatcher와 Controller의 분리 (공통적으로 쓰는 디스패처를 하나만 두고 Controller를 따로 모아 놓음)
* Controller는 POJO클래스로 서블릿이 아닌 일반 클래스로 만듦
* 사용자 요청이 들어오면 디스패쳐가 사용자 요청을 수반해 적절한 컨트롤러를 찾아서 수행하게 진행함
> Controller는 로직에 해당되는 뷰를 적당하게 호출할 수 있도록 디스패쳐에게 관련내용을 알려주면 디스패쳐에서 그걸 가지고 호출함 (front controller)
* 일단 프론트컨트롤러는 제외하고 컨트롤러와 뷰만 분리된 형식으로 만들기
> 포워딩과 redirect의 차이
forward : 현재 작업한 내용을 이어갈 수 있게 공유하는것
redirect : 현재 작업내용 공유 상관없이 새로운 요청
> 포워딩 쓰기
1.RequestDispatcher dispatcher = request.getRequestDispatcher("spage.jsp");
현재 spage라는 URL(@WebServlet매핑)이 있는데 요청이 들어오면 jsp로 요청을 전달하는 것
spage.jsp명만 넣는 이유는 URL상 같은 디렉토리에 있다고 생각하기 때문에 지정하지 않음
2. dispatcher.forward(request, response);
현재 작업한 내용을 spage.jsp로 넘어가면 내용을 이어줌 (jsp지만 결국 서블릿으로 변환됨)
jsp에서 쓰고 있는 request, response가 포워드시 전달된 것과 동일함
일을 이어갈 수 있음
3. result 값을 같이 넘겨줘야 하므로
이때 사용되는 저장소 request임
(어떤 상태 저장하기 위한 저장소 내부객체)
pageContext : 서블릿 페이지 내에서 혼자 사용함
request : 포워드 관계에서 사용할 수 있는 저장소
session : 현재 세션에서 공유될 수 있는 저장소
application : 모든 페이지에서 공유될 수 있는 저장소
cookie : 클라이언트에 저장하는 저장소 (서버상에서는 쿠키는 제외하고 총 4개임)
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=kimkwon429&logNo=220760918355 참조하기
4. 그럼 실행은 어디서? >>> 무조건 컨트롤러에서 실행해야함 (컨트롤러에서 데이터를 만들기 때문이다)
5. 하나 아쉬운점
자바 코드블럭의 흔적이 남아있음
<body>
<!-- result == Model -->
<%=request.getAttribute("result")%>
입니다.
</body>
6. EL표기법을 이용해 해결
< ------------------------------------------------------------ View를 위한 데이터 추출 표현식 EL(Expression Language) ------------------------------------------------------------>
* JSP를 MVC 방식으로 만들때 요긴하게 쓰인다.
* EL은 지역변수를 이용하기 위한 목적이 아니라 applicaiotn, session, request, pageContext와 같은 저장소에 있는 값을 꺼내서 출력하는 목적이다.
기존의 방식
<Controller> <> <view>
request.setAttribute("cnt", 50); request.getAttribute("cnt")
EL 표기법으로 변환
request.setAttribute("cnt", 50); ${cnt} >> "키"값만 쓰면 된다(변수명이 아니라)
<리스트를 쓴다면?>
List list = new ArrayList() {"1", "test"...}; ((List)request.getAttribute("list")).get(0) // Object타입 반환하므로
request.setAttribute("list", list);
EL 표기법으로 변환
List list = new ArrayList() {"1", "test"...}; ${list[0]} // 자동적으로 타입변환을 해주고 get(0)을 배열처럼 사용가능 (Array(일반배열)도 동일 )
request.setAttribute("list", list);
<Map을 쓴다면?>
Map n = new HashMap("title", "제목"); ((Map)request.getAttribute("n")).get("title") // Object타입 반환하므로
request.setAttribute("n", n);
EL 표기법으로 변환
${n.title} // 키와 value값이므로 .연산자를 이용해 불러옴 AttrName.MapKey
< ------------------------------------------------------------ EL의 데이터 저장소 ------------------------------------------------------------>
* ${cnt}라는 키워드는 request라 하는 저장소에 담겨있다고 말함
* 사실은 request저장소에서만 ${cnt}키워드를 검색하는것은 아님
* 실제로 서버상에 존재하는 사용가능한 저장소는 4개 (page(페이지 내에서 사용할 수 있는 서블릿 객체들을 모아놓음), request, session, application) 이것으로 데이터를 꺼내거나 저장함
* pageContext (하나의 JSP페이지에서 공유가능)
jsp 파일에서 pageContext page = setAttribute();로 값을 세팅하면 그 페이지에서 사용가능 >> EL로도 쓸 수 있다.
* EL을 이용해 뽑아낼수있는 객체는 pageContext, request, session, application 어디에 담겨있는것도 뽑아낼 수 있다.
* 만약 4개의 저장소에 같은 키워드 값을 가진 값을 심으면? 어떻게 될까?
>> 오류가 나지 않으므로 충돌이 나지 않는거고 이는 곧 우선순위가 있다.
* EL이 저장소를 찾는 우선순위 (먼저 찾게되면 뒤에있는 값을 뒤지지 않음)
1. pageContext
2. request
3. sesison
4. application
>> 따라서 page, request에 둘다 있는 값을 꺼내고 싶으면 request값은 절대 못뽑음
* 이를 해결하기 위한 방법 scope (특정한 저장소에서만 값을 꺼내오는 키워드,한정자)
>pageScope (page에서만 값을 꺼내옴)
>requestScope (request에서만 값을 꺼내옴)
>sessionScope (session에서만 값을 꺼내옴)
>applicationScope (application에서만 값을 꺼내옴)
ex) ${sessionScope.cnt} (객체가 아니라 한정자다)
>> 이는 객체가 아니라 한정자이다. (해당저장소 객체의 기능 쓸수없음)
* EL표기는 4대 저장소 외에도 쓸수있는 다양한 객체 존재
param, paramValues, header, headerValues, cookie, initParam, pageContext
> 사용방법
parameter얻기위해선 : ${param.cnt}
header 값얻기위해선 : ${header.host}
header에서 변수명명규칙에 어긋나면 : ${header["host"]}
>pageContext 사용방법
<%=pageContext.getRequest().getMethod() %> >> ${pageContext.request.method}
(현재 request값을 받고 java>jsp로 넘겨줄때 방식이 post인지 get인지 반환)
EL표기는 자바를 쓰는 느낌이 아니라 객체를 쓸수있게 해주기 위해
getter를 쓰긴하지만 메소드 호출느낌을 없애기 위해 괄호와, get을 빼고 소문자로 바꿈
< ------------------------------------------------------------ EL 연산자 ------------------------------------------------------------>
[] .
()
not ! empty
* / div % mod
+ -
< > <= >= lt(lessThan) gt(greaterThan le ge
== != eq ne
&& and
|| or
? :
왜 굳이 두 종류의 연산자를 사용할까?
html은 <>를 가지고 있으므로 안에서 <,>를 쓰는것이 바람직하지 않다 (xhtml : xml기반 문서는 허용안할수도있음)
그냥 편한거 쓰다 쓸수없는 환경이면 바꿔쓰자~!
* empty 연산자
${empty num} == ${num==null || num==""} (null이거나 빈문자면 참값 반환)
반대의 동작을 원하면 not empty 이용
3항연산자를 이용해서 같이 사용가능
${empty param.n? '값이 비어있습니다.' : param.n }
* 만약 ${param.n / 2}
문자열이라 오류?
정수 나누기 정수라 소수점 떼어내기?
실수?
>> 정답은 실수로 나타남 만약 소수점을 떼어내고 정수로만 쓰고싶다? 좀 더 특별한 방법 필요 : JSTL
< ------------------------------------------------------------ JSP를 이용해서 자바 웹 프로그램 만들기 시작 ------------------------------------------------------------>
html > jsp로 변경하면 한글 깨짐 alt + enter >> 인코딩 변경해준다
< ------------------------------------------------------------ JDBC를 이용해 글 목록 구현하기, 자세한 페이지 구현하기 ------------------------------------------------------------>
* JDBC 드라이버 추가하기
톰캣으로 배포해서 실행하므로 자바 프로젝트에 추가하는게 아니다
웹에서 사용되는 라이브러리가 어딘가로 배포되어 실행되어야 하므로 라이브러리를 같이 가지고 가야함
라이브러리를 같이 배포할 수 있도록 라이브러리를 포함시켜야함
WEB-INF에 담아준다
< ------------------------------------------------------------ 자세한 페이지 MVC model1 방식으로 변경하기 ------------------------------------------------------------>
* 위에서 작성한 코드는 일명 스파게티 코드.. 자바 코드 블록이 이곳저곳 떨어져 있어서 가독성이 매우 떨어진다. >> 한번 꼬이면 전체 코드를 찾으면서 고쳐야함 >> 굉장히 어렵다.
>> 이게 가능하려면 html에 꽂아넣을수있는 최소한의 변수를 만들고 데이터만 꽂아넣는 구조 >> MVC model 1이다 >> 물과 기름처럼 양분하는데 가장 큰 역할 == model(변수)
* HTML : View
* JAVA CODE : Controller
<<한번 짚고 가자 Servlet Annotation의 의미>>
잘못 답변 하신듯 합니다.. 질문의 내용은 list.jsp의 a태그에서 detail만 지정했는데 님이 말씀하신 왜 같은 경로의 detail.jsp가 아닌 com.newlecture.web.controller 패키지의 NoticeDetailController 컨트롤러가 정상적으로 먼저 호출되었는가? 일거에요.. 그 답변으로 실제 존재하지 않는 notice 경로에 detail 이 있다고 말씀하신 것이구요. detail.jsp라면 몰라도.. 저 분의 질문이 제가 이해한게 맞다면 .. NoticeDetailController 컨트롤러 페이지에 서블릿 어노테이션으로 매핑한 주소 @WebServlet("/notice/detail") 이것이 지정되어 있기 때문에 가상의 경로(강의 초반에 있습니다.)를 url로 매핑되어 있기 때문에
NoticeDetailController 컨트롤러의 url 상 주소는 @WebServlet("/notice/detail") 즉 /notice/detail 가 되는 것이죠. 그럼 list.jsp역시 notice의 경로에 있으니 detial.jsp가 아닌 'url'상의 경로 (실제 소스폴더 경로가 아님) 즉, NoticeDetailController 가 호출된 것입니다.
간단하게 설명 드리지 못해 죄송합니다..
정리드리면,
NoticeDetailController.java 파일의 웹서블릿어노테이션 = @WebServlet("/notice/detail") 이것을 url로 표현하면 => http://.../notice/detail
list.jsp는 detail.jsp를 먼저 호출하지 않고 NoticeDetailController 가 호출돼야 하므로 본인의 경로 /notice 는 생략하고 detail을 호출하므로 http://.../notice/detail 됩니다..
http://.../notice/detail = NoticeDetailController.java
출처 : https://www.youtube.com/watch?v=p2g7pB7SbYw&list=PLq8wAnVUcTFVOtENMsujSgtv2TOsMy8zd&index=56
< ------------------------------------------------------------ MVC model2 방식으로 변경하기 ------------------------------------------------------------>
* model1에선 model이란 녀석을 지역적으로 사용함 controller는 자바코드, view는 html이다
* 지역적으로 사용했다는 의미는 하나의 페이지에서(하나의 함수)에서 mvc만 구현했었음
* model2는 이걸 나누겠다는 의미 (Controller(servlet), Model, View(JSP) ) >> 어떻게 보면 서블릿이 2개이므로 모델의 서블릿과 서블릿을 이어주는 방법과 서블릿에서 모델 값을 넘길때 공유방법이 필요함
* 나눠서 작성하면 복잡도가 높아질수도 있지만 나눠서 만드는 장점이 크면 만들 수있음
* 장점 : V,C의 개별적으로 유지관리 가능 (협업가능), 재사용의 용이성, 실행에도 용이성이 있는데 Controller를 떼어놓음으로서 (자바코드만있음) 미리 컴파일 가능해짐
* 따라서 Controller가 Model을 만들어 상태를 저장해서 View단 서블릿으로 넘겨주어야 하는데 이때 상태저장하기위한 객체
1. pageContext : 얘는 안됨
2. request : 제일 적합함 request는 입력도구지만 저장도구로 쓸수있는 능력도 있다.
3. session : 범위가 넓음
3. application : 범위가 넓음
* JSPPrj에서 제대로 context root 를 '/'제대로 설정했다면 /의 의미는 webapp/ 경로를 나타낸다.
따라서 Controller의 URL매핑을 /notice/detail로 두면 webapp/notice/detail에 있다고 생각할 수 있다.
detail.jsp는 list.jsp가 선행되어야 id값을 받아서 게시물을 출력할 수 있으므로 먼저 선행해서 실행한다.
list.jsp의 a태그의 href옵션은 detail.jsp?id=<%=rs.getInt("id")%>가 아니라 detail?id=<%=rs.getInt("id")%> 이렇게 Controller의 좌표를 넘겨주고
Controller에서 Model값들을 request에 저장해 detail.jsp로 forward한다
< ------------------------------------------------------------ Model 데이터를 구조화 하기 ------------------------------------------------------------>
request.setAttribute("title", title);
request.setAttribute("regdate", regdate);
request.setAttribute("memberId", memberId);
request.setAttribute("hit", hit);
request.setAttribute("files", files);
request.setAttribute("content", content);
* 지난 시간에 출력을 위해 사용했던 데이터를 한 단어로 말하면?? ----> 공지사항!
이렇게 개념적으로 말 할 수 있는 데이터의 집합을 -------> 개체(Entity):개념화된 데이터, 다른사람들은 사용자형 자료형으로 따로 형식을 만들어 정의하기도함
-------> Entity, 개체, 개념화된 데이터 > 묶어서 계층을 만들었다해서 구조적인 데이터라고 말하기도함 (분야, 상황에 따라 용어 달라짐)
* 프로그램 만들며 낱개로 설정된 데이터들을 낱개로 사용하면 ------> 반복되는 코드 많아짐, 데이터 구분 어려워짐, 데이터 표현할때 이름 명명시 다른 개념의 데이터와 충돌 해결위해 이름이 길어짐
따라서 데이터를 낱개로 보기보다 데이터를 속성이란 이름으로 대치하고 이걸 묶어서 표현하는 방식이 좋아보임
-------> request.addAttribute("notice", notice); ----> 객체를 만들어 전달하면 훨씬 더 간단하고 직관적으로 표현 가능
* 이렇게 담으면 EL 표기법을 쓸 수 있음
Notice : Entity클래스가 있고 거기에 Getter가 존재하면 아래와 같이 표기가능
<%=((Notice)request.getAttribute("notice").getId() %> -------> ${notice.id}
(EL표기는 자바를 쓰는 느낌이 아니라 객체를 쓸수있게 해주기 위해 getter를 쓰긴하지만 메소드 호출느낌을 없애기 위해 괄호와, get을 빼고 소문자로 바꿈)
< ------------------------------------------------------------ 목록페이지(list.jsp) MVC Model2로 수정하기 ------------------------------------------------------------>
* list.jsp는 반복을 돌면서 List 객체에서 Notice Entity를 하나씩 사용하는데
* EL은 반복문을 처리하는 기능이 따로 없다.
* 따라서 따로 태그 라이브러리를 사용하지만 일단 자바코드로 대체한다
* 다음부터는 Controller를 먼저 만들고 view를 생성하는 일반적인 방식으로 !
< ------------------------------------------------------------ View 페이지 은닉하기 ------------------------------------------------------------>
* Controller와 View단을 분리했지만 자꾸 실행을 View에서 실행하려고 하는 관성이 붙어있음 ----> 에러 발생 (NullPointException) Controller에서 Model을 만들지 않았으므로
* 또한 사용자가 요청할 수 있는 디렉토리에 포함되어 있으므로 위험함 (view는 사용자가 직접 요청하면 안됨)
* Controller를 통해서만 출력할 일이 있을때 선택적으로 실행한다. ---> 출력에 대한 복잡도가 있기 때문에 분리한것
* 따라서 이런일을 방지하기 위해 사용자가 직접 요청할 수 없는곳에 두는게 바람직해 보임
* WEB-INF : 이는 외부에 서비스 되지 않는 파일들이 존재함 (설정 파일이나 라이브러리, 코드파일을 둠)
> 이제 여기에 View페이지를 둔다 따라서 사용자가 직접 요청할 수 없게 한다. (Controller에 의해 출력되어야하지 사용자가 임의로 출력할수없게함)
> WEB-INF내 view라는 폴더 만들고 view페이지들을 옮겨담는다
> 이후 만들어놓은 Controller들의 디스패쳐 경로를 다 수정한다
> view에서 직접 요청하게 되면 404 오류(서버에서 제공되지 않는페이지) 발생
> view페이지에서 은닉해도 css, js파일은 절대경로(webapp/ 디폴트)로 지정하면 연결이 끊기지 않는다
< ------------------------------------------------------------ View에서 흐름 제어를 위한 자바 코드 블록 제거하기 ------------------------------------------------------------>
* list.jsp에서 자바코드를 이용하고있는데 이를 대체할 무엇인가 필요 -- > 태그를 이용한 제어구조로!
<forEach> for each문을 태그로 대신할 수 있음
</forEach>
라이브러리 다운로드 필요 >> JSTL API
<c:forEach var="notice" items="${list}"> // items="${list}"에 저장소에 있는것을 이름값을 이용해 뽑아서 담을수있음,
//반복될때마다 꺼낼때 이용되는 그릇은 var="" >> var 키워드로 pageContext 저장소에 담는다 따라서 EL표현식 이용가능
</c:forEach>
>> 자바코드화 하는것을 대신해주는 역할을 해줌
< ------------------------------------------------------------ Tag 라이브러리와 JSTL(JSP Standard Tag Library) ------------------------------------------------------------>
* view단에서 사용되는 제어를 담당하는 태그들을 모두 제공해줌
* 크게 5가지 범주의 태그라이브러리 제공
1. core : 가장 기본적인 제어의 행위 담당
2. Formating: 날짜, 화폐, 숫자 등을 포멧할 수 있는 기능 담당
3. Functions : EL을 이용해 데이터를 추출해 가끔은 문자열을 쪼개거나 소문자를 대문자로 바꾸는 문자열 조작이 필요할때 사용할 수 있는 함수들의 묶음
4. SQL : SQL 이용가능 (쓰지 않는게 바람직)
5. XML : XML 이용가능 (쓰지 않는게 바람직)---> 전체적인 코드의 구성이 깨짐
* 태그를 쓸 땐 접두사가 필요 <c:TAG>
> 만약 접두사를 사용하지 않으면 jasper는 태그에대해 이해 불가
> 반드시 jasper가 이해하고 이 태그는 행위를 담당하는 녀석이라는걸 이해할 수 있어야함
> 따라서 접두사와 uri를 이용함
> uri는 정의하고 있는 태그가 식별할 수 있게 (다른사람이 만든것과 충돌하지 않게) 도메인 name을 붙임
> uri를 이름앞에 붙이기엔 너무 길어서 prefix="c"는 곧 uri를 의미하므로 이를 대신 태그앞에 식별자로 붙임 <c:forEach>
> 따라서 Jasper 가 구분 가능해짐
> 만들어 보고싶으면 인터넷 찾아보셈~!
< ------------------------------------------------------------ 중간정리 요약 정리 ------------------------------------------------------------>
* 서블릿 -> 자바 웹 서버 프로그램 (자바 + 웹 기반 프로그래밍)
* 웹 -> 입/출력 -> (request (입력설정, 입력값 읽기)
(response(출력설정, 출력스트림) --> 사용자에게 보내는게 문자열이아닌 html을 보냄 (html태그출력 불편) ----> 웹문서 출력 -----> 문서 기반(.jsp)의 코드블록
* 코드블록 多 -> 스파게티 코드 됨 -> 해결하기 위한 MVC패턴 --> view에서 코드 블록이 꼭 필요한 경우 ---> EL, JSTL이 자바 코드블록을 대신해줌
< ------------------------------------------------------------ JSTL : forEach의 속성 사용하기 ------------------------------------------------------------>
<c:remove>
<c:choose>
<c:otherwise>
<c:forEach>
<c:url>
<c:set>
<c:if>
<c:when>
<c:forTokens>
<c:catch>
* forEach에서 처음부터 끝이 아닌 어떤 범위를 한정지을 수 있다. >> begin, end
> <c:forEach var="notice" items="${list}" begin="0" end="3">
> 여기서 begin, end의 값은 반복할때 사용되는 인덱스의 값을 이용함
> index값을 얻는것은 varStatus 옵션에서 얻을 수 있다.
><c:forEach var="notice" items="${list}" begin="0" end="3" varStatus="st">
> 이렇게 주면 st라는 이름의 변수를 태그내에서 사용가능
> 위의 그림처럼 forEach의 상태객체에 변수명을 st로 설정(varStatus="st")하면 그 변수명을 통해서 다음처럼 상태 값들을 이용할 수 있다.
아래는 st의 옵션
${st.current} 현재 반복되는 아이템 (반복되는것이 숫자면 숫자, 객체면 객체가됨)
${st.index} 현재 반복되는 반복 인덱스(0부터 시작함)
${st.count} 현재 반복 횟수
${st.first} 현재 아이템이 첫번재 아이템인지를 알려줌(true 또는 false)
${st.last} 현재 아이템이 마지막 아이템인지를 알려줌(true 또는 false)
${st.begin} begin 속성에 설정한 값
${st.end} end 속성에 설정한 값
${st.step} 반복되는 인덱스의 증가치(step옵션으로 1씩 증가뿐아니라 다른 수도 가능)
>> 반복이 이루어질때 상태값 활용해야하면 적절히 활용가능
< ------------------------------------------------------------ JSTL : forEach문으로 Pager 번호 만들기 ------------------------------------------------------------>
* forEach문에서 값을 받아올 객체가 없다면 begin end로 반복문을 돌릴 수 있다.
* begin end로 반복횟수를 성정하고 var로 변수를 선언하면 begin부터 시작해서 반복하며 end까지 값이 차례로 저장된다. 사용할때는 $ {i}로 EL표현식으로 사용
* 하지만 pager번호 즉 사용자가 12345중 하나의 페이지를 요구하면 1~5까지 페이지가 보이고, 8이면 678910의 페이지가 보여야함 즉 pager 번호가 필요함
* 일단 시작 번호를 구해야함 > 시작번호 = 현재 페이지 번호 - (현재페이지번호 % 5 - 1)
* 그렇다면 page 변수도 필요해짐!
* 어떻게 구현?? > c:set 태그 사용!
set태그에 사용되는 변수는 pageContext에 저장됨 > 같은 페이지내에서 사용가능
<c:set var="page" value="${(param.p == null) ? 1: param.p}"/> >> page변수에 get방식으로 받은 p의 값을 받아옴 하지만 최초 페이지를 열경우 p값은 null이므로 1페이지를 보여준다
<c:set var="startNum" value ="${page - (page % 5 - 1) }"/> >> 불러온 page값을 이용해 startNum을 구한다. 이는 forEach태그 내에서 페이져 번호와 현재 페이지 번호를 보여준다.
> 결국 forEach태그내에서 ${startNum + i}로 쉽게 사용 가능하다.
< ------------------------------------------------------------ JSTL : if 문으로 Pager 이전/다음 번호 만들기 ------------------------------------------------------------>
* 다음페이지로 넘어가기 위해서는 p값을 그 다음페이지의 값으로 변경해줘야함
* 위의 i의 가장 큰 값보다 하나 더 큰값을 주어야 다음페이지로 넘어가짐 i의 가장 큰값 = 4이므로 5를 더함
* <a class="btn btn-next" href="?p=${5+startNum}&t=&q=">다음</a>
<span class="btn btn-next" onclick="alert('다음 페이지가 없습니다.');">다음</span>
* 하지만 위의 두 태그를 배타적으로 만들기 위해서는 하나가 선택되면 다른것이 선택되면 안됨 -----> if문
* JSTL은 else문은 없으므로 if문을 두개 이용해서 베타적으로 만들어야함
* 조건을 달 때 JSTL자체는 연산자를 가지고 있지 않다 --> 내부옵션에서 조건식을 쓸 때(test옵션) 속성에 들어가는게 연산자라면 EL표기를 통해 처리함
* 하지만 마지막 페이지를 알기위해서는 DB의 레코드수와 관련있으므로 일단 보류
* 만약 23페이지면 위에서 구현한 5+startNum이 lastNum보다 더 커지게 되면 마지막 페이지라는 알림을 표시한다.
* <c:if test=""> </c:if> test에는 조건식이 들어가는 경우 EL표기를 이용해야함, else 문은 없음
< ------------------------------------------------------------ JSTL : forTokens로 첨부파일 목록 출력하기 ------------------------------------------------------------>
* 첨부파일을 올릴때 목록을 view에서 어떻게 출력할 것인지
* <c:forTokens var="fileName" items="${notice.files}" delims=","> >> delims는 items에 들어온 문자열값을 끊는 기준 (여기서는 쉼표 기준) 따라서
토큰이란 어떤 문장이 내가 원하는 기준으로 잘랐을때 잘라진 각 하나의 단위이다.
forTokens는 토큰을 만들면서 반복을 한다.
토큰을 이용하기위해 변수를 이용
<td colspan="3">${notice.files}</td>
</c:forTokens>
< ------------------------------------------------------------ JSTL : format태그로 날짜 형식 변경하기 ------------------------------------------------------------>
* 상단에 접두사를 하나 지정해야함 보통 fmt로 이용
* <fmt:formatDate pattern="" value="${notice.regdate}"/>
pattern에 특정 키워드를가지고 출력형식 지정
ex) "yyyy-MM-dd hh:mm:ss" (달과, 분이 m이므로 달은 M 분은 s로 표기
< ------------------------------------------------------------ JSTL : 숫자 출력 형식 지정하기 formatNumber ------------------------------------------------------------>
* 조회수와 같은것들이 자릿수가 높아질수록 표현하는 형식을 지정하면 좀 더 가독성이 좋을거같음
* <fmt:formatNumber type="number" pattern=",####" value="${notice.hit}"/> >> type=패턴의 타입지정, pattern 어떻게 표현할지, 포멧팅할 값
< ------------------------------------------------------------ JSTL : functions로 EL에서 함수 이용하기 ------------------------------------------------------------>
* 업로드한 파일목록에서 출력만 대문자로 출력하고 싶은경우 >> functions 이용
* 상단에 jstl functions를 추가하고 원하는 EL태그 안에서 이용할 수 있다.
<a href="${fileName}">${fileName}</a> --소문자를대문자로 출력만 하고싶은 경우---> <a href="${fileName}">${fn:toUpperCase(fileName)}</a>
* 특정 글자만 색깔을 다르게 한다던지?
<c:if test="${fn:endsWith(fileName, '.zip') }">
<c:set var="setStyle" value="font-weight:bold; color:red;"/>
</c:if>
위와같이 조건을 두고 원하는 곳에 꽂아 쓴다 근데..!!!
안된다 .zip파일이 아니라 모든 파일에 적용됨 왜 그럴까?
< ------------------------------------------------------------ 기업형으로 레이어를 나누는 이유와 설명(코드 분리를 위해) ------------------------------------------------------------>
1. 서블릿으로만 프로그램 만듦
2. 서블릿과 문서출력을 나눔 (Servlet, JSP) Servlet은 사용자의 응답 처리 및 출력(view)주도, 업무처리담당, JSP는 오직 view만 >> 혼자 개발, 혼자서비스하면 바람직한 모델
3. 사람이 여러명으로 나누어서 만드는 경우
업무서비스(트랜잭션), Servlet, 문서출력을 나눔 --> 분업유리, 업무서비스 큰 변동적으므로 화면단 변경시 부담 내려감 (기업형)
> 업무서비스란? : 사용자의 요청 단위(계좌이체, 결제 등등)
4. DB 활용시 데이터베이스 서비스(DAO(CRUD))도 업무서비스에서 분리될수도있다.
사용자 --> Servlet <--Model(업무결과에 대한 데이터)---> 업무서비스 <-----Entity(Java형태로 개념화,구조화된 데이터)-----> 데이터 서비스(DAO) <------> DBMS
|
|
|
V
브라우져<--문서 출력(.jsp)
>> 필요에따라 업무서비스와 데이터서비스를 합치거나, 서블릿과 업무시비스를 합쳐서 사용할 수 도있다.
< ------------------------------------------------------------ 서비스 함수 찾아내기 ------------------------------------------------------------>
* 서블릿에서 업무 서비스를 분리할것이다.
* 분리해서 별도의 클래스를 만들고 데이터베이스를 이용하는 코드를 옮길것
* 서블릿은 업무 서비스를 이용해 데이터베이스 서비스를 받음
* 시스템 단위로서 서비스 클래스를 만듬
* 현재 우리 시스템은 공지가 있다. 관리자-----> (공지등록, 공지일괄공개, 공지수정, 공지삭제(일괄삭제)), 회원-------->(공지목록조회, 공지상세조회)
* 관리자는 회원의 기능을 물려받지만 추가로 기능이 있다.
* 구체적인 서비스명이나 정의방법은 일단 공지목록조회, 공지상세조회 (사용자 요청이 제일 중요함(요청에 수반한 데이터제공, 데이터 수반한 문서 제공)
* 사용자는 페이지 요청을해 공지 목록을봄
* 사용자는 페이지를 클릭해 다른 페이지 목록을 보여줌
* 사용자는 검색을 요청함
* 서비스함수(공지사항 목록)
>> 문서, 페이지, 검색을 요청할때 서비스해줘야하는것은 Notice임
>> getNoticeList() (notice 목록을 줌)
>> getNoticeList(int page) (원하는 페이지의 notice 목록을 줌)
>> getNoticeList(String field, String query, int page) (사용자가 원하는 검색어의 notice 목록을 줌)
>> 또한 전체 페이지의 개수를 얻기 위한 getNoticeCount(), getNoticeCount(String field, String query)
* 서비스함수(특정 공지사항 페이지 요청)
>> id를 넘겨받음 getNotice(id)
>> 다음목록, 이전목록 (getNextNotice(id), getPrevNotice(id))
*이렇게 페이지를 만들때 어떤 데이터가 필요하면 그걸 역할자에게 달라고 하면됨
>> 이런 제어를 Controller가 담당함(역할자를 부려먹음)
< ------------------------------------------------------------ 서비스 클래스 구현하기 ------------------------------------------------------------>
* 새로운 패키지를 만듬 --> NoticeService
< ------------------------------------------------------------ 서비스 클래스 구현을 위한 SQL코드 작성하기 ------------------------------------------------------------>
* getNoticeList에서 사용된 날짜가 정렬되고 새로운 ROWNUM 을 붙인 쿼리가 필요
* select *
from (select ROW_NUMBER() over(order by n.regdate desc) rownum, n.* from NOTICE n) n2
where n2.rownum between ? and ?;
< ------------------------------------------------------------ getNextNotice, getPrevNotice 메소드의 SQL 쿼리 작성하기 ------------------------------------------------------------>
1. mysql에서.. 다음 게시물 id얻기 위해선 먼저 현재 게시물 전체를 날짜 내림차순 정렬후
2. 가져온 현재 id로 위의 결과물을 가지고 날짜를 내림차순으로 정렬하고 첫번째 요소를 뽑아오면됨
select *
from (
select ROW_NUMBER() over(order by n.regdate) rownum, n.*
from ( select *
from notice
where regdate > (select regdate
from notice
where id = 3)) n) n2
where rownum = 1;
반대로 이전 게시물을 구하는 방법은 조금만 변형하면된다
select *
from (
select ROW_NUMBER() over(order by n.regdate desc) rownum, n.*
from ( select *
from notice
where regdate < (select regdate
from notice
where id = 3)) n) n2
where rownum = 1;
< ------------------------------------------------------------ getNoticeList의 JDBC 코드 구현하기 ------------------------------------------------------------>
* DB 관련내용은 서비스로 옮기고 컨트롤러에선 과감하게 삭제
* 이렇게 컨트롤러는 사용자와 상호작용에 포커스를 두고
* 데이터 서비스는 서비스 모듈을 따로두고 데이터 서비스를 전문적으로 한다.
< ------------------------------------------------------------ NoticeService 클래스 완성하기 ------------------------------------------------------------>
* 이전 시간과 거의 동일한 작업들 제일 중요한건 쿼리식, 그리고 데이터를 잘 꽂고 잘 전달하는것이다
< ------------------------------------------------------------ 목록 페이지에서 검색 추가하기 ------------------------------------------------------------>
* 검색어를 계속 남기기 위해선 value="${param.[name]}값을 꽂아 넣는다.
* form태그에서 action의 디폴트값은 submit을 누른페이지, method의 디폴트값은 get 방식이다
* 하지만 목록에서 페이지가 구현되지 않음 --> 이전 다음 안넘어가지고 검색한 값들 초기화됨
< ------------------------------------------------------------ 목록에서 페이징 구현하기 ------------------------------------------------------------>
* 신경쓸게 정말 많다 사소해 보이지만 사용자 입장에선 큰 차이라는거 알아두기 데이터가 m v c를 왔다갔다하는데 놓치는것이 없는지 확인하기
String field = "title";
// 꼭 null과 "" 체크 둘다 해주기!!
if (field_ != null && !field_.equals(""))
field = field_;
String query = "";
// 꼭 null과 "" 체크 둘다 해주기!!
if(query_ != null && !query_.equals(""))
query = query_;
int page = 1;
// 꼭 null과 "" 체크 둘다 해주기!!
if(page_ != null && !page_.equals(""))
page = Integer.parseInt(page_);
< ------------------------------------------------------------ Pager에서 현재 페이지 번호처리 ------------------------------------------------------------>
* EL은 홑따옴표도 가능하다
* 페이지 처리에서 EL표기법이 많이 사용됨을 알 수 있다. 따라서 많이 보고 연습해두자
< ------------------------------------------------------------ Pager에서 마지막 번호 처리하기 ------------------------------------------------------------>
* EL에서 나눗셈은 실수로 나옴 따라서 페이지 처리를 위해 정적메소드 Math.ceil(10.2) ---> 11이 되게 만들 수 있다.
> 하지만 11.0이 되므로 JSTL functions를 이용해 소수점을 자른다.
* ${fn:substringBefore(Math.ceil(count/10), '.')}
count/10의 결과를 올림하고 올림한 값에서 .아래의 값은 버리고 정수를 만든다.
* lastNum이 구현 완료되면 그걸 이용해 현재 페이지 보이는것과 이전, 다음을 구현
< ------------------------------------------------------------ 자세한 페이지 수정하기 ------------------------------------------------------------>
* 자세한 페이지(detail.jsp) >> Controller과 업무서비스 분리
* 기존의 값 삭제 기존에 만든 service함수를 기준으로 작성한다.
< ------------------------------------------------------------ 댓글 수를 포함하기 위한 쿼리 문제 ------------------------------------------------------------>
-- MySQL확인하기
< ------------------------------------------------------------ 목록의 댓글 수를 위한 view 생성하기 ------------------------------------------------------------>
* MySQL확인하기
* 컬럼을 추가하기 위한 정도로만 뷰를 생성해야함
* NOTICE_VIEW를 생성해 getNoticeList() 쿼리를 뷰를이용하는것으로 변경, 다만 content는 용량이 크므로 뷰에 담기기힘들어서 뺌
> 그러면 NOTICE_VIEW는 새로 추가된 칼럼값이 생감 CMT_CNT 이를위해 Notice entity에 추가해야할까?
> NOTICE_VIEW를 담을 entity를 만듬 (Notice를 상속을 받는다)
> 이후 getNoticeList()는 List <Notice>가 아닌 List<NoticeView> 타입으로 변경한다 그에따른 content값은 지워준다
< ------------------------------------------------------------ INDEX 페이지 추가하기 ------------------------------------------------------------>
* MVC 구현으로 --> controller 구현 및 그에따른 view를 구현한다
* IndexController 구현하고 --> index.jsp로 forward함
* Controller의 수가 많아지며 복잡도가 올라감 따라서 패키지로 정렬한다
com.hoseok.web.controller > com.hoseok.web.controller.notice에 기존 list, detail 컨트롤러를 넣고 이름을 변경 ->DetailController.java, ListController.java
< ------------------------------------------------------------ Admin 페이지를 위한 서비스 목록 추가하기 ------------------------------------------------------------>
* 관리자 페이지는 일괄공개, 일괄삭제, 글쓰기 기능이 있음
일괄공개 : pubNoticeAll(ids)
일괄삭제 : removeNoticeAll(ids)
* 관리자는 글을 등록할수도 있음 공지등록 요청 : insertNotice(notice)
* 글 상세 조회시 목록 뿐아니라 수정, 삭제를 할 수 있다. : deleteNotice(id) 수정은 수정페이지로 넘어감
* 수정페이지에서 수정요청이 들어감 : updateNotice(notice)
* 관리자 외에도 Notice를 이용해 데이터를 보여주는곳이 또 있음 index.jsp에서 최신 공지사항 보여줌 : getNoticeNewestList()
pubNoticeAll(ids)
removeNoticeAll(ids)
insertNotice(notice)
deleteNotice(id)
updateNotice(notice)
getNoticeNewestList()
위를 구현함
< ------------------------------------------------------------ admin/index 페이지 추가하기 ------------------------------------------------------------>
* 관리자 페이지의 index를 추가해야함
* 컨트롤러의 이름이 동일하면 서버에서 구분을 하지 못하므로 url에 직접 요청
< ------------------------------------------------------------ admin/notice/list 페이지 추가하기 ------------------------------------------------------------>
* 관리자에서 list, detail.jsp는 수정 삭제 권한이 있는것이 다르므로 따로 만들어줘야함
* 그에따른 com.hoseok.web.controller.admin.notice라는 패키지를 만들어 관리
* 기존에 만든것들을 많이 활용 list.jsp의 내용 및 ListController의 내용 >> jstl구문 및 기존의 것들을 활용하여 생성
< ------------------------------------------------------------ 다중 선택 값 POST하기 ------------------------------------------------------------>
* 24강에서 다중값 post 방식 설명함
* 일단 체크 박스에 공개하고자 하는 게시물의 값들을 전달 value="" summit button을 눌러야지 전달됨 (따라서 포함되는 태그 전부에 form태그로 묶어야함)
* 하지만 일괄공개, 일광삭제 버튼을 누르면 공개, 삭제에 해당하는 체크박스의 값들이 모두 전달되므로 잘 구분해야함
오류 404 : URL이 없어서 발생하는 오류
오류 405 : URL이있지만 메소드가 없는 경우
오류 403 : URL, 메소드 있지만 권한이 없으면
* 서버쪽에서는 값을 어떻게 받을까? : 같은 키값이면 배열형식으로 온다
> 우선 doPost메소드를 구현
> 서버에 printf로 찍어봄
* 하지만 서버쪽에서는 view에서 어떤걸 눌렀는지 구분해야함
> view에서 한 버튼만 눌러도 모든 값이 전달되므로
< ------------------------------------------------------------ 다중 Submit 요청 구분하기 ------------------------------------------------------------>
* submit버튼도 value값을 보낸다, 다만 name값을 써줘야함
> 값이 둘 중 하나만 가므로 name은 동일하게 하고 서버쪽에서 name의 값의 종류로 로직 나누기
* 또한 공개와 비공개를 표기하기위한 pub 컬럼 추가하기 0과 1로 나뉨
< ------------------------------------------------------------ 일괄삭제 구현하기 ------------------------------------------------------------>
* 삭제시 컨트롤러는 받은 목록으로 삭제하는것이 아니라 서비스에게 요청 -> service에게 삭제요청을함
* 이후 사용자에게 지워진 것을 확인할 수 있는 get요청을 다시 실행 (response.sendRedirect("list") --> get방식으로 호출함, 클라이언트가 아닌 서버쪽에서 서버를 요청하듯이 이용)
* 하지만 delId는 기존 정수형이던 ID값이 String 값이므로 정수형으로 명령어를 보낸다. (String->Integer일괄 변경 방법은 여러개가 있지만 간단하게 for문 이용)
* service 함수에서 id를 꽂을때 문자열 변수에다 for문을 돌며 id값들을 더한다 (숫자 -> 문자열 자동변환)
* 실행시 자꾸 같은이름 충동리 발생할때 URL 변경하기 귀찮음 -> webapp내 가짜 파일 만듦 그리고 실행하려는 파일 경로명과 동일하게 만들고 실행하면 controller가 실행됨
< ------------------------------------------------------------ 공지사항 등록을 위한 Controller/View 준비하기 ------------------------------------------------------------>
* 공지사항 등록 컨트롤러 만들기 : com.hoseok.web.admin.notice.RegController.java
* 이 컨트롤러는 페이지 요청때 get 요청과 글쓰기 누르고 글등록 post 요청 모두 필요함
* 경로는 /admin/board/notice/reg --> 실제위치에 jsp 파일 있는지 확인후 없으면 만듦
* doPost는 데이터를 바로 받아옴 (등록요청이므로) title, content, isOpen받음
> 이때 생각할게 post시 전달받을값이 없을때 기본값이 필요한가??
> title, content는 일단 필요없음 내용이없으면 등록이 안되므로, isOpen 도 굳이..
> 따라서 값을 직접 받아온다
* checkbox는 value값을 가져오는게 체크 됐으면 가져온다
* form의 action태그의 url은 그 default값은 reg라는 값을 갖고 페이지 요청했으므로 reg가 그대로 요청되지만 호환성 위해 적어줌
* 그리고 화면에 뿌리는 PrintWriter를 이용해 체크해봄 --> 이때 isOpen은 체크했을때와 언체크일떄 값이 true와 null이므로 조건문으로 나눠주는것이 좋음
* 또한 한글 등록시 한글이 깨점 --> filter적용해야할듯 근데 난 적용 했는데 왜 깨지지???!
< ------------------------------------------------------------ pub 컬럼 추가에 대한 변경사항 처리 ------------------------------------------------------------>
* pub 컬럼 추가시 수정사항 발생함 --> 만만치 않음
> isOpen 컬럼을 추가해야하므로 수정이 꽤 많아짐 먼저 entity에 컬럼 변수 추가..
> 생성자 재정의후 getter setter 다시만듬
> notice_view 뷰수정
> notice_view를 다루는 NoticeView entity객체도 수정 (꼬리에 꼬리를뭄)
> 그리고 NoticeService.java도 수정함 pub 컬럼 추가
* 두번째로 값을 받아와서 다시 출력할때 (PrintWriter.out.printf()) 한글이 깨짐 --> 필터를 적용해야함
-> request가 문제가 되는게 아니라 출력을 하는게 문제이므로 response로 utf-8지정을 해줘야함
< ------------------------------------------------------------ 공지사항 등록하기 ------------------------------------------------------------>
* 공지사항 등록전 NoticeService.java 완성함 또한 RegController에서 isOpen 조건 검사 및 변환 해야함
* 그리고 현재 관리자 페이지를 만들고있으므로 작성자가 누구인지 인증과 권한이 중요함
> 사용자가 전달하는것이 아니라 시스템 자체적으로 현재 글을 쓰는 사람이 누구인지 알 수 있어야함
> // 로그인 처리를 구현하게 되면 인증을 처리한 사용자의 아이디로 바뀜
notice.setMemberId("hoseok");
* 등록설정까지 마치면 RegController.java에서 insertNotice로 등록을 마치고(result로 확인까지) 이후 sendRedirect로 list를 호출
* 경로지정안하고 호출하면 자신의 url에 마지막에 list로 바귐 (reg -> list)
* 이후 list.jsp에서 글쓰기 경로를 reg.html -> reg로 reg.jsp에서 취소 경로를 list.html -> list로 변경
* 그리고 글쓰기 실행하면 등록됨!
< ------------------------------------------------------------ 공지사항 자세한 페이지 추가(detail.jsp) ------------------------------------------------------------>
* 기존의 페이지를 이용하고 각 버튼들의 링크를 잘 수정하면 됩니당~!
< ------------------------------------------------------------ 파일업로드를 위한 인코드 multipart/form-data ------------------------------------------------------------>
* 글쓰기 -> 파일선택후 글쓰기 등록을 하고 개발자 도구 켜보기, Form Data의 view source를 보면 문자열만 보내짐 즉 파일은 안보내짐
* form 태그의 옵션중 enctype="application/x-www-form-urlencoded" >> 값들이 전달될떄 post든 get이든 쿼리스트링 값을 만들듯이 값이 만들어진다
>> 하지만 이렇게해서는 파일명만 전송되므로 파일을 절대 전송할 수 없음
* file을 전송하기 위해서는 enctype="multipart/form-data"를 이용하면됨
>> 이후 개발자 도구 확인해보면 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPgxhkiMGlIAgY4sx 이렇게 나타남
>> 근데 글이 등록이 안됨 (title에 null을 삽입할 수 없다 >> 인코딩 방식만 바꿨는데 읽지 못함) >> 실제로 확인해도 null값이 나타남
< ------------------------------------------------------------ 파일업로드를 위한 서블릿 설정 ------------------------------------------------------------>
* 지난시간에는 클라이언트 설정을 했고 이번엔 서버측 서블릿 설정을 해야함.
> 방법은 web.xml설정, annotation설정 두가지 방법이 있다. >> 구글로 찾아보기
* RegController.java >> 이곳에 annotation을 설정함
@MultipartConfig (
//location="/tmp",
fileSizeThreshold=1024*1024, locatoin과 fileSizeThreshold는 연결되어있음 이는