-
22-09-20_jspmain 2022. 9. 20. 19:11
오늘부터 3 tier(3계층) 구조로 확장하게 됨.
원래는 client-server 간 2 tier 구조였는데, 이제 DB라는 tier를 하나 추가해서 구동해 보도록 하겠음.
-
먼저 톰캣의 내용을 좀 살펴보겠음.
정적 요청을 처리해주는 default servlet 클라이언트로부터 발생하는 모든 동적 요청 중 jsp는 여기서 처리 tomcat이 WAS가 될 수 있었던 이유는 정적 요청과 동적 요청 이 두가지를 다 처리할 수 있었기 때문
--
오늘 볼 것중
Persistant Layer에서 어떻게 DB라는 tier에 명령을 내릴건가
- P.L의 중요한 역할 중, DB에서 넘어오는 메세지 표현방식을 어떻게 java 방식으로 바꿀지 이런 것도 중요.
우리가 사용하는 mariaDB, oracle ==> 모두 RDMBS - ERD(관계형 데이터베이스)임.
DB에서 가져온 내용들을 java에서 표현할 수 있는 Domain Layer가 필요
이것도 오늘 볼 것.
이제 P.L에서 B.L (business logic layer)로 넘어갈 때
cursor 형태였다가 java 객체 형태로 넘어오게 되고...
B.L에서 Controller Layer(C.L)에서 V.L (Vew Layer)로 넘어가서 contents 화 하고, 이 때 쓸 수 있는4가지 scope (flow control 구조를 통해서 이동함), 이제 V.L에서 가장 먼저 할 일은, scope에 attribute 상태로 존재하는 정보를 꺼내서 content화 해서 response 에 body영역에 method형태로 들어감.
--
오늘부터는 이제 DB와 PL사이에 데이터가 어떻게 오가는 지 볼 것.
--
지금까지는 전날 수업 리뷰를 하고 수업을 했는데, 오늘은 오늘 수업을 먼저 나가고 그 이후에 오늘 수업과 이전 수업을 연계하는 내용을 해보자.
=====
--
떨어진 두개의 tier 사이에 통신을 하려면 통신이 필요
socket 통신은, 어느 한쪽에서든 상대방을 대상으로 통로를 개설해야 함.
- 상대방 시스템이 어떻게 돌아가는지, port번호가 어떤지에 따라서 방법이 다 다름..
그러면 상식적으로 우리가 두 가지 DB를 가짐(mariadb, oracledb), 그런데 application은 java로 만든 하나가 있다면, socket을 가지고 통신을 직접 열어야 한다고 하면,
0
mariadb랑 처음에 통신을 개방한다 하면 maria랑 연결하는 소스코드가 java에 있는데, 갑자기 oracledb를 쓰게 된다고 하면.
동작하는 소스코드를 다 바꿔야 함.
그런데 우리 코딩 원칙중에 제일 중요한게, 이미 짜 놓은 최대한 코드를 뜯어고치지 말아야
그래서 뜯어고치지 않는 방법을 배워보겠음.
리모콘이 사용된다고?????????? 리모콘이 뭐라고요?????
---
DB와 연결을 하기 위해 필요한 Connection 클래스가 어떤 놈인지 검색해봅시다
어라? 그런데
요놈은
interface예요.
클래스가 아니라서 정확한 내용이 정의되어있지 않아요
자세히 볼까여?
리모콘(실체 수행하는 driver) 자체가 아니라 interface로써 공통의 동작방식만 지정
ctrl을 누르고 connection 눌러서 보면, delegate만 있고 oracle 관련된 건 없음.
==> 특정 db에서만 사용할 수 있는 거 아님..
(oracle 관련된 내용들이 있다고 하면, 특정 db에서만 사용 가능한 내용이니까. )
'리모콘을 누른다'는 방식만 interface로 지정되어 있는거지 리모콘도 빔프로젝터도 실체가 없는 거임.
그러면 db와 modeler를 연결하기 위해 가장 먼저 뭐를 해야 할까???
빔프로젝터는 db임. -- 내가 java를 통해서 접근해서 조작하고자 하는 개체임.
사용해야되는 나 - client나 사용자, user
리모콘을 누른다는 동작은(interface) 아는데 리모콘이 필요함
그게 드라이버임.(driver)
여태는 직접 수립한 connection을 이용해서 해 왔는데, (db에 직접 연결하는 코드를 자바소스에 작성해서 연결을 사용해 왔음)
-- 이 방식의 경우 소스에 DB의 접근방식이나, url, 아이디 등 내용들을 다 작성해 주어야 하기 때문에
그르면 db종류가 바뀔 때 마다 내 코드도 다 바뀌어야 함...
근데 우리는 코드를 최대한 수정하지 않는 방식을 해야 하잖아요?
그래서 이걸 해결하기 위해 드라이버가 필요하고.. (아래에서 사용하는 DB의 종류에 맞게 다운받을거임)
이걸 하기 위해...
퍼사드패턴(Facade Pattern) 이라는 디자인패턴이 많이 사용됨..
* 퍼사드패턴(Facade Pattern) : 클래스 라이브러리 같은 어떤 소프트웨어의 다른 커다란 코드 부분에 대한 간략화된 인터페이스를 제공하는 객체이다.
간략하게 말하자면 나는 facade pattern에 명령을 내리면 쟤가 db에 대신 명령을 내려주는 구조임 리모콘같은느낌인거지
자 oracle 폴더로 가봅시다
어디있는지 모르겠다고요? 아래의 명령어로 찾아봅시다.
위치는 cmd에서 where sqlplus 명령어를 이용하여 여기서 찾으세용 아래의 경로를 타고 들어가서 열어보면 jar들이 있는데
이 jar들이 우리가 oracle에서 사용할 수있는 드라이버(리모콘)들임. 저 중에 ojdbc6.jar를
여기 library folder에 넣어줍니다 ---
jar 엄청 많져? 실제 필드에 나가서도 이렇게 많아여
그런데 하나하나 넣으면 빡세겠죠?
그래서 maven이라는 build 관리 툴을 사용해요
( maven : jar관리, 프로젝트 만들 때 사용 ** 잘 기억해두기)
--
oracle db를 이용하기 위한 리모콘 하나 찾았으니 mariadb를 이용하기 위한 다른 리모콘도 넣어주자
저거 를 사용하기 위한 리모콘을 찾으러 가자
mariaDB의 홈페이지에 가서 봅시다 (이런 드라이버들은 벤더들이 배포하기때문)
MariaDB Foundation - MariaDB.org
… Continue reading "MariaDB Foundation"
mariadb.org
이렇게 와서 보면 뒤에 J가 붙은게 java랑 연결하기 위한 애들이예요 아 저기 이상하네 다운이 안되네요 다른 사이트로 가자
Open Source Database (RDBMS) for the Enterprise | MariaDB
MariaDB provides open source database and database as a service (DBaaS) solutions to support scalability, mission-critical deployments, and more.
mariadb.com
저기 가서 resources의 Connectors에 접속해서 보면 https://mariadb.com/docs/connect/#mariadb-connectors
Open Source Database (RDBMS) for the Enterprise | MariaDB
MariaDB is the leading enterprise open source database with features previously only available in costly proprietary databases. Enterprise grade, wallet friendly.
mariadb.com
https://mariadb.com/docs/connect/programming-languages/java/
Open Source Database (RDBMS) for the Enterprise | MariaDB
MariaDB is the leading enterprise open source database with features previously only available in costly proprietary databases. Enterprise grade, wallet friendly.
mariadb.com
위의 링크 중 하단의 링크를 통해서
java-jdbc에 버전 맞는거 다운.
우리는 이 버전임
MariaDB Connector/J 2.7
이제 다운로드 링크 (Installation)
https://mariadb.com/downloads/connectors/connectors-data-access/java8-connector/
Download MariaDB Connectors for data access & streaming | MariaDB
Download connectors for high-performance data access & data streaming. MariaDB connectors include Python, C, C++, Java 7, Java 8, ODBC, R2DBC and Node.js.
mariadb.com
여기서 2.7.6-GA 버전...으로 다운로드
짠 oracledb의 jar파일과 마찬가지로 좌측에서 library 폴더에 jar파일을 넣어줌. 이제 이 드라이버를 이용해서 db에 명령을 내리도록 해야 함..
--
web이 아니라 테스트 케이스를 만들자
--
단위테스트(Unit Test) - 클래스나 메서드 단위에서 정상동작여부를 판단하기 위한 테스트 - 를 하기 위한 거임
, junit이라는 프레임 워크 사용
ctrl+N를 이용하여 만들자.
이렇게 만들거예요 저 아래에 선택하는 옵션들이 있는데, 크게 보면 두가지. 하나는 setup옵션, 하나는 tearDown 옵션인데, 두 가지 분류는 무슨 역할을 하냐면, 우리는 지금 test case를 통해서 우리 code의 적절성을 시험하려는건데, setUp은 test 전에 시험, tearDown은 테스트 종료되고 마지막으로 확인할 거 있을때 사용함.
- 자세한 실행순서는 아래에 -
DB...에 대한 정보는.. JVM에 한번만 알려주면 되는데 --> 가장 먼저 실행되는 setUpClass에다가 코드를 작성해서 알려주면 됨.
beforeClass annotation을 쓰면, 아래의 두개의 클래스가 실행되기 전에 딱 한 번, beforeClass 된 게 실행됨.
beforeClass는 전체 클래스가 실행되기 전에 사전에 한 번 실행됨.
그런데
저 Before, After는 테스트 클래스의 갯수만큼 실행되고 AfterClass는 최종에 한 번만 실행 됨. 즉,
위의 예제 실행순서.
[setUpClass → {setUp → (testOracle) → tearDown} → {setUp → (testMariaDB) → tearDown} → tearDownClass]
요런 순서로 실행되는거임
이제 이거 적어줄건데
이건 뭐냐면, oracle이라는 패키지에 jdbc가있고 그 안에 driver있고 그 안에 OracleDriver라는 클래스? 뭐... 어쨋든 그렇게 실행됨... 이게 내가 불러오고 싶은 걸 순차적으로 타고 내려가는걸 저렇게 적어왔던 건데,
무슨 소린지 모르니까 아래의 gif이미지를 통해서 쉽게 이해합시다.이걸 근데 언제 하나하나 손으로 쓰겠음? 실체를 찾아보겠음.
oracleDriver가 어디에 있는지 위치를 찾아보면 이렇게 되고, 저렇게 주소를 복사해서 쓰면 됨 마찬가지로 mariadb driver도 어디에 있는지 이렇게 찾을 수 있음. 이렇게 두개.
이 리모콘(드라이버)에 대한 정보는 한번만 알려주면 되서 BeforeClass annotation 설정된 메서드에서 작성 해 줬음.
이릏게 찾아옴 qualified name을 copy해서 paste하면 우리가 평소에 손으로 하나하나 적던 걸 저렇게 이용할 수 있음. 손으로 하나하나쓰지말곻 quailfied name copy 해서 씁니다
이건 이제 우리가 쓰는 java의 대부부니 저 rt.jar 에있는데 저기보면 이제 인터페이스처럼 있음. 그리고 위의 gif에서 보면 rt.jar에서 java.sql에 가서 보면
대부분 인터페이스로 구성되어 있음. 즉 사용방법만 있고 실체는 없다는 이야기임.
저 사용방법의 실체가 우리가 위에서 찾은 jar파일들(db의 jar들)이 구현체를 주는거임.
이제 test 코드를 마무리해봅시다 작성하는 중간내용은 생략해버리고 약간의 주석으로 대체했어요 수업 너무빠르다
package kr.or.ddit.jdbc; import static org.junit.Assert.*; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class DataBaseConnectionTest { private Connection oracleConn; private Connection mariaDBConn; //jvm에게 jar추가됐음을 알리기 //어플리케이션 전체를 통틀어 한번 @BeforeClass public static void setUpClass() { try { Class.forName("oracle.jdbc.driver.OracleDriver"); Class.forName("org.mariadb.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } @Before public void setUp() { //이 물리적 주소만 가지고는 DB 식별 불가, 포트번호 필요 (:1521) //ip address나 domain address (@//localhost) String oracleURL="jdbc:oracle:thin:@//localhost:1521"; String oracleUser="ddit"; String oraclePassword="java"; String mariaDBURL="jdbc:mariadb:@//localhost:3305"; String mariaDBUser="root"; String mariaDBPassword="python"; try { oracleConn = DriverManager.getConnection(oracleURL,oracleUser,oraclePassword); mariaDBConn = DriverManager.getConnection(mariaDBURL,mariaDBUser,mariaDBPassword); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testOracle() { System.out.println(oracleConn); } @Test public void testMariaDB() { System.out.println(mariaDBConn); } @After public void tearDown() { try { if(oracleConn!=null) oracleConn.close(); if(mariaDBConn!=null) mariaDBConn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @AfterClass public static void tearDownClass() { System.out.println("테스트 종료"); } }
코드 마무리하고 test 실행해서 보면
junit 보면 1차적으로는 잘 된 것 같음 console도 보겠음
잘나옴 나이스하네
우리가 콘솔에 나온 저 주소를 가지고 오려고 db 연결해서 여태 썼던거임.
근데 또 보자,
package kr.or.ddit.jdbc; import static org.junit.Assert.*; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class DataBaseConnectionTest { private Connection oracleConn; private Connection mariaDBConn; //jvm에게 jar추가됐음을 알리기 //어플리케이션 전체를 통틀어 한번 @BeforeClass public static void setUpClass() { try { Class.forName("oracle.jdbc.driver.OracleDriver"); Class.forName("org.mariadb.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } @Before public void setUp() { //이 물리적 주소만 가지고는 DB 식별 불가, 포트번호 필요 (:1521) //ip address나 domain address (@//localhost) String oracleURL="jdbc:oracle:thin:@//localhost:1521"; String oracleUser="ddit"; String oraclePassword="java"; String mariaDBURL="jdbc:mariadb://localhost:3305"; String mariaDBUser="root"; String mariaDBPassword="python"; try { oracleConn = DriverManager.getConnection(oracleURL,oracleUser,oraclePassword); mariaDBConn = DriverManager.getConnection(mariaDBURL,mariaDBUser,mariaDBPassword); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testOracle() { String sql = "SELECT MEM_NAME FROM MEMBER"; System.out.println(oracleConn); } @Test public void testMariaDB() { String sql = "SELECT E_NAME FROM EMP"; System.out.println(mariaDBConn); } @After public void tearDown() { try { if(oracleConn!=null) oracleConn.close(); if(mariaDBConn!=null) mariaDBConn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @AfterClass public static void tearDownClass() { System.out.println("테스트 종료"); } }
이런식으로 쓸 건데 중간데 test 클래스들 보면, 이건 자바인데 저기서는 sql문쓰죠 다른 언어를 써서 사실 이렇게는 실행 안 됨. 그래서 query 객체를 사용할 거임
@Test public void testOracle() { String sql = "SELECT MEM_NAME FROM MEMBER"; try { oracleStmt = oracleConn.createStatement(); System.out.println(oracleConn); } catch (SQLException e) { e.printStackTrace(); } } @Test public void testMariaDB() { String sql = "SELECT E_NAME FROM EMP"; try { mariaDBStmt = mariaDBConn.createStatement(); System.out.println(mariaDBConn); } catch (SQLException e) { e.printStackTrace(); } }
우리 많이 보던 stmt져? (Statement객체) 저거예요 (자매품, preparedStatement)
--query를 실행하는 excute계열 메서드들도 쓸 것.
select가 하나고(excuteQuery), 나머지가 하나로(excuteUpdate) 또 묶임, ....
code assistant에서 확인해보면
excuteQuery는 sql select 문을 위해 typically하게 제공되는거고 excuteUpdate는 insert, update, delete sql문을 위해서 제공되는 메서드임. 왜 저렇게 둘로 쪼개지는가?
jdbc driver 입장에서는
insert, update, delete 다 똑같이 수정쿼리임 (excuteUpdate 하나로 통일)
select는 ResultSet...에 저장되는데
index 없이 집합의 형태로 위치함. pointer 형태로 위치해 있어서 header에 cursor 가 가있음.
그래서 .next()메서드를 써 줘야 cursor가 내려와서 필요한 컬럼들을 뽑아 낼 수 있음
돌아오는 객체가 몇개가 될 지 몰라서 set으로 받음. 또 중복도 허용.....
@Test public void testOracle() { String sql = "SELECT MEM_NAME FROM MEMBER"; try { oracleStmt = oracleConn.createStatement(); ResultSet rs = oracleStmt.executeQuery(sql); while(rs.next()) { System.out.println(rs.getString("MEM_NAME")); } System.out.println(oracleConn); } catch (SQLException e) { e.printStackTrace(); } } @Test public void testMariaDB() { String sql = "SELECT E_NAME FROM EMP"; try { mariaDBStmt = mariaDBConn.createStatement(); ResultSet rs = mariaDBStmt.executeQuery(sql); while(rs.next()) { System.out.println(rs.getString("E_NAME")); } System.out.println(mariaDBConn); } catch (SQLException e) { e.printStackTrace(); } }
이렇게.
쿼리문 사용할 수 있는 객체를 이용해서 수정해줌.
이렇게 이제 사용을 했으면 query 객체자원들이 열린채로 남아있지. 이거 닫아줘야 함.
@After public void tearDown() { try { if(oracleStmt!=null) oracleStmt.close(); if(oracleConn!=null) oracleConn.close(); if(mariaDBStmt!=null) mariaDBStmt.close(); if(mariaDBConn!=null) mariaDBConn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
먼저 stmt 닫아주고 connection 닫아줌 (사용한거 먼저 닫고 연결을 마지막에 끊어주는게 맞지)
실행해보자
package kr.or.ddit.jdbc; import static org.junit.Assert.*; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class DataBaseConnectionTest { private Connection oracleConn; private Connection mariaDBConn; private Statement oracleStmt; private Statement mariaDBStmt; //jvm에게 jar추가됐음을 알리기 //어플리케이션 전체를 통틀어 한번 @BeforeClass public static void setUpClass() { try { Class.forName("oracle.jdbc.driver.OracleDriver"); Class.forName("org.mariadb.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } @Before public void setUp() { //이 물리적 주소만 가지고는 DB 식별 불가, 포트번호 필요 (:1521) //ip address나 domain address (@//localhost) String oracleURL="jdbc:oracle:thin:@//localhost:1521"; String oracleUser="ddit"; String oraclePassword="java"; String mariaDBURL="jdbc:mariadb://localhost:3305/python"; String mariaDBUser="root"; String mariaDBPassword="python"; try { oracleConn = DriverManager.getConnection(oracleURL,oracleUser,oraclePassword); mariaDBConn = DriverManager.getConnection(mariaDBURL,mariaDBUser,mariaDBPassword); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testOracle() { String sql = "SELECT MEM_NAME FROM MEMBER"; try { oracleStmt = oracleConn.createStatement(); ResultSet rs = oracleStmt.executeQuery(sql); while(rs.next()) { System.out.println(rs.getString("MEM_NAME")); } System.out.println(oracleConn); } catch (SQLException e) { e.printStackTrace(); } } @Test public void testMariaDB() { String sql = "SELECT E_NAME FROM EMP"; try { mariaDBStmt = mariaDBConn.createStatement(); ResultSet rs = mariaDBStmt.executeQuery(sql); while(rs.next()) { System.out.println(rs.getString("E_NAME")); } System.out.println(mariaDBConn); } catch (SQLException e) { e.printStackTrace(); } } @After public void tearDown() { try { if(oracleStmt!=null) oracleStmt.close(); if(oracleConn!=null) oracleConn.close(); if(mariaDBStmt!=null) mariaDBStmt.close(); if(mariaDBConn!=null) mariaDBConn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @AfterClass public static void tearDownClass() { System.out.println("테스트 종료"); } }
이래해서 또 수정을 했는데요, 이제 실행
이렇게 실행된당 mariaDB에 있는 컬럼의 내용과 Oracle DB에 있는 컬럼의 내용을 모두 조회해서 확인해 볼 수 있다.
-----
==========================================
JDBC(Java DataBase Connectivity) - java와 DB를 연결하는 방법론
1. 드라이버를 빌드 패스에 추가(vendor(홈페이지) 제공) 2. 드라이버 로딩(언제, 몇 번? 어플리케이션 시작 시점에 한 번) 3. Connection 수립 4. 쿼리 객체 (생성) Statement PreparedStatement (선 컴파일 된 쿼리 객체, (객체가 생성될 때 컴파일이 완료된, 언제인지 바꿀수없다고)) ibatis는 주로 얘를 사용 CallableStatement (프로시져나 함수와 같은 절차 코드 호출에 사용.) (function, procedure의 공통점은? 여러 개의 명령어들을 하나의 절차로 캡슐화 해 놨음. ) 그리고 그 절차를 호출 할 수 있음. 그러면 둘의 차이점은? function은 함수라서 return 타입이 존재, procedure는 return타입없음. 그래서 이 단점 때문에 호출자에게 data를 전달하는 다른 방법이 필요한데, 인자를 넘길 수 있음. 인자는 크게 in bound parameter out bound parameter있음. out bound는 java로 치면 call by reference, in bound는 call by value 방식임 그러면 객체의 상태가 변경이 되면 call by reference는 호출자에게도 전달, call by value는 값을 복사해서 가져오는거라 원래의 값이 변경되어도 호출자에게 전달되지는 않음. ) 5. 쿼리 실행 ResultSet을 반환, executeQuery : SELECT //ibatis로 치면 query for Object같은거 int를 반환, executeUpdatae : INSERT/UPDATE/DELETE (변한 것 갯수int) //IBATIS로 지면 UPADATE,INSERT같음 //ibatis에서 insert는 일반적인 메서드와는 동작방식이 다름(object반환함) //insert, / update, delete 둘 서로 좀 다름, 나중에 알아서 공부해보세요 6. 쿼리 실행 결과 처리 7. 자원 해제(close), (*** 가장 중요)
마지막 자원 해제가 아주 중요한데
- 계속해서 connection을 설정하고 close를 안하면 언젠가 부하가 걸려서 실행이 안되겠져?
특히, 우리가 사용하는 express version은 session을 동시에 30개밖에 생성을 못함 .그래서 close가 더 중요함
--이제 미션이예요
REFACTORING 작업을 할것인데요
먼저 DB를 확인합시다.
이케 잔뜩나오죠 select해서 확인할 컬럼의 이름들을 쿼리문에 지정해주고요...
지금 위에 쿼리문으로 출력되는 결과를 브라우저에 출력하는게 미션이예요
--메타데이터도 활용이 되어야 해요
테이블네임이 쿼리문에 있는 이름이니깐...
return type을 확인해보면 ResultSet이져 이걸 활용하면 가능할거예요
-----
VO를 활용하면 좋고요. 왜냐면 저 안에 이미 우리가 꺼내기로한 컬럼들이 저기 객체화되어서 들어계시잖아요? vo가 가진 구조와 우리가 사용할 테이블의 구조가 같음을 알 수 있음. (우리가 꺼낼 것도 Description, PropertyName, PropertyValue고, VO에 있는 필드들도 Description, PropertyName, PropertyValue임) 네 완성이되었습니다.
<%@page import="java.util.ArrayList"%> <%@page import="kr.or.ddit.props.vo.PropertyVO"%> <%@page import="java.util.List"%> <%@page import="java.sql.DriverManager"%> <%@page import="java.sql.SQLException"%> <%@page import="java.sql.ResultSetMetaData"%> <%@page import="java.sql.ResultSet"%> <%@page import="java.sql.Statement"%> <%@page import="java.sql.Connection"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%! private Connection oracleConn; private Statement oracleStmt; private ResultSet rs; private ResultSetMetaData rsmd; public void init() throws ServletException{ try { Class.forName("oracle.jdbc.driver.OracleDriver"); //드라이버 로딩한거로 커넥션 수립 } catch (ClassNotFoundException e) { e.printStackTrace(); } } %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>10/jdbcDesc.jsp</title> </head> <body> <h4>JDBC(JavaDataBaseConnectivity)</h4> <pre> 1. 드라이버 빌드 패스에 추가(vendor 제공) 2. 드라이버 로딩(어플리케이션 시작 시점에 한번) - 지금 위리가 사용하는 드라이버 버전이 4세대 이후라서 로딩 안해도 실행됨. 3세대 이전 버전이면 에런라 수 있음. 3. Connection 수립 4. 쿼리 객체 Statement PreparedStatement(선컴파일된 쿼리 객체) CallableStatement(프로시져나 함수와 같은 절차 코드 호출에 사용.) 5. 쿼리 실행 ResultSet executeQuery : SELECT int executeUpdate : INSERT/UPDATE/DELETE 6. 쿼리 실행 결과 처리 7. 자원 해제(***) </pre> <% try { Class.forName("oracle.jdbc.driver.OracleDriver"); //드라이버 로딩한거로 커넥션 수립 } catch (ClassNotFoundException e) {} String oracleURL = "jdbc:oracle:thin:@//localhost:1521/XE"; String oracleUser = "ddit"; String oraclePassword = "java"; String sql = "SELECT PROPERTY_NAME, PROPERTY_VALUE, DESCRIPTION FROM DATABASE_PROPERTIES"; List<PropertyVO> dataList = new ArrayList<>(); //아래에서 다 더해줘서 정보를 dataList객체가 다 가지게 됨 String[] headers = null; //지역변수 초기화를 위해 null값 넣어줘야 try( Connection oracleConn = DriverManager.getConnection(oracleURL, oracleUser, oraclePassword); Statement oracleStmt = oracleConn.createStatement(); ) { rs = oracleStmt.executeQuery(sql); rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); headers = new String[count]; //DB는 index가 1부터 가니까 i가 1로 시작 for(int i = 1; i <= count; i++){ //배열은 java기준이라 0부터 시작하니까 i-1 headers[i-1] = rsmd.getColumnName(i); //아래에서 있어질 table header값도 배열로 미리 받아두는게 가능하죠 } //쿼리 실행결과 담고 활용 while(rs.next()){ PropertyVO vo = new PropertyVO(); dataList.add(vo); vo.setPropertyName(rs.getString("PROPERTY_NAME")); vo.setPropertyValue(rs.getString("property_value")); vo.setDescription(rs.getString("DESCRIPTION")); //인덱스 주는 것보다 명확하게 컬럼 이름 주기 //camel표기법, snake표기법 표기법이 있어서 상호간 유추 가능 //reflection 기술만 있으면 이걸 수동으로 안해도 된다는거지 //그걸 활용해놓은게 ibatis라는 녀석? //자바는 대소문자 구분 안 해서 소문자로 PROPERTY_VALUE 적어줘도 됨 } } catch (SQLException e) { e.printStackTrace(); } %> <table border="1"> <thead> <tr> <th><%=headers[0] %></th> <th><%=headers[1] %></th> <th><%=headers[2] %></th> </tr> </thead> <tbody> <% for(PropertyVO vo : dataList) { %> <tr> <td><%=vo.getPropertyName() %></td> <td><%=vo.getPropertyValue() %></td> <td><%=vo.getDescription() %></td> </tr> <% } %> </tbody> </table> </body> </html>
이렇게 짜면
이렇게 웹에서 우리가 보고자 하는 내용들을 확인할 수 있음. 근데 이게 그렇게 좋은 코드는 아님
왜냐면 model1 구조임. 모든 소스와 코드가 하나의 클래스에 다 들어가 있어서 어질어질하거든요
모듈화가 안 되어 있어서 유지보수도 어렵고
----
View Layer는 content만드는거
model2 구조는 mvc 패턴에...
???
--
FileSystemPropertyDAO.java 이거 쓰던걸 File...에서 데이터를 불러와서 작업했던 이전 파일인데요
이제DB에서 이 property를 관리할 수 있는 구조가 되게 바꾸는거예요...
-관리되고 있는 data에 맞춰서 객체화 시킬 수 있는 Domain view인, PropertyVO 여기 보면
private String propertyName;
private String propertyValue;
private String description;이 세가지 담을 수 있는 구조가 나옴 (DB의 data를 객체로 가져올수있거든요)
---
이제 해당 DB에 접근할 수 있는 persistant Layer 필요, 그게 이DAO인데
DB에 접근할 수 있는 구조...
interface에 새로운 구현체 만들면 됨 -->> 할일1
이렇게 persistance Layer가 재료를 가져옴
--
이제 요리하는게 businessLayer임, PropertyServiceImpl
재료가 어디에 있는 요리과정이 바뀌지는 않음. 그러면 DB에 접근한다 해도 service를 바꿀 필요는 없음
이제 이 앞에 주문 받을 controller layer있어야 함, PropertiesControllerServlet
db가 바뀌었다고 해도 얘를 바꾸진 않아도 됨. 그러면 controller도 그대로 써도 됨
이제 table을 차려야 함. 그건 view layer에서 함
우리 일단 지금 무조건 jsonview쓰고있음, JsonViewServlet,
재료의 위치가 바뀌었어도 이 view를 바꾸진 않아도 됨.
---
DB로 바뀌면서 해야 할 건 persistant layer만 데이터 저장소 구조에 맞게 새로운 구조로 끼워넣어주기만 하면 됨.
응집력이 높음.
Logic은 business logic layer만 가짐. 안건드려도 됨
marshalling 역할도 명확히 구분됨 손 안대도 됨
심지어 dao interface쪼개놓음
기존의 구현체를 손 안대도 되고, interface의 새로운 구조의 dao를 만들어서 추가 해 주면 됨
--> 기존의 코드 수정이 없음
DataBasePropertyDAOImpl 를 추가해주자
원래는 model1이었음. jdbcDesc.jsp에 다 넣었음.
이제 여기서 테이블 태그를 구성하는 요소는 그대로 두고,
try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (ClassNotFoundException e) {} String oracleURL = "jdbc:oracle:thin:@//localhost:1521/XE"; String oracleUser = "ddit"; String oraclePassword = "java"; String sql = "SELECT PROPERTY_NAME, PROPERTY_VALUE, DESCRIPTION FROM DATABASE_PROPERTIES"; List<PropertyVO> dataList = new ArrayList<>(); String[] headers = null; try( Connection oracleConn = DriverManager.getConnection(oracleURL, oracleUser, oraclePassword); Statement oracleStmt = oracleConn.createStatement(); ) { rs = oracleStmt.executeQuery(sql); rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); headers = new String[count]; for(int i = 1; i <= count; i++){ headers[i-1] = rsmd.getColumnName(i); } while(rs.next()){ PropertyVO vo = new PropertyVO(); dataList.add(vo); vo.setPropertyName(rs.getString("PROPERTY_NAME")); vo.setPropertyValue(rs.getString("property_value")); vo.setDescription(rs.getString("DESCRIPTION")); } } catch (SQLException e) { e.printStackTrace(); }
이 코드만 가져온다.
아래처럼 새로운 구현체를 완성해준다.
@Override public List<PropertyVO> selectProperties() { try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (ClassNotFoundException e) {} String oracleURL = "jdbc:oracle:thin:@//localhost:1521/XE"; String oracleUser = "ddit"; String oraclePassword = "java"; String sql = "SELECT PROPERTY_NAME, PROPERTY_VALUE, DESCRIPTION FROM DATABASE_PROPERTIES"; List<PropertyVO> dataList = new ArrayList<>(); String[] headers = null; try( Connection oracleConn = DriverManager.getConnection(oracleURL, oracleUser, oraclePassword); Statement oracleStmt = oracleConn.createStatement(); ) { ResultSet rs = oracleStmt.executeQuery(sql); ResultSetMetaData rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); headers = new String[count]; for(int i = 1; i <= count; i++){ headers[i-1] = rsmd.getColumnName(i); } while(rs.next()){ PropertyVO vo = new PropertyVO(); dataList.add(vo); vo.setPropertyName(rs.getString("PROPERTY_NAME")); vo.setPropertyValue(rs.getString("property_value")); vo.setDescription(rs.getString("DESCRIPTION")); } return dataList; } catch (SQLException e) { e.printStackTrace(); //예외가 발생한 경우에, 여기선 지금 return이 없는데, // return null 이라고 하면, 발생한 예외 정보 사라지고, 어디서 null생긴지 모름 // interface의 예외의 종류를 unchecker로 바꿔 주어야 함 throw new RuntimeException(e); } }
--이제 미션이예요
Properties파일이 아니라, DB의 property name이 와야 함. 딱 한줄의 코드만 바꾸면 됨. 어디를 바꿔야 할 지 찾아보세요
HCLC : 응집력 높이고 결합력 낮춤.
Layered architecture를 구성할 때...
Layer 간 의존관계 형성되어 결합력이 발생함.
우리가 새로 만든 consistance layer, 얘가 의존하고 있는 건 business layer임
이걸 의존하고 있는거임 이걸 바꿔 주어야 함 이렇게 새로 만든 걸 참조하도록 바꿔 주면 바뀐 저장소에 따라서 뜨는 정보가 달라진다. 저장소(DB)를 바꾸니까, service 등 수정해야 되는 상황이 생김....의존성 때문임.
---
이제 또 미션임
--여기서 propertuy를 선택하면 에러가 남.
아직 property를 조회하는 query문이 완성이 안 되어 있기 때문임.
두 번째 미션은.
이걸 수정해서 상세정보가 나오도록 하는 것
----
항목 누를 때마다 null값 나옴 <%@ 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> <jsp:include page="/includee/preScript.jsp" /> </head> <body> <select id="propSel" size="10"></select> <table> <thead> <tr> <th>프로퍼티명</th> <th>프로퍼티값</th> <th>비고</th> </tr> </thead> <tbody id="dataArea"> </tbody> </table> <form id="propForm" action="<%=request.getContextPath() %>/property" method="post"> <input type="text" name="propertyName" placeholder="프로퍼티명"/> <input type="text" name="propertyValue" placeholder="프로퍼티값"/> <input type="submit" value="새 프로퍼티 추가" /> </form> <div id="errArea"></div> <script type="text/javascript"> $(function(){ //현재 페이지에서 발생하는 모든 비동기 요청에 대한 처리 //에러 처리 한번에 할 거임 let errArea = $("#errArea"); $(document).ajaxError(function(event, jqXHR, thrownError){ //마지막 5번에서 하려고 한 걸 이 안에서 공통적으로 처리해 주면 됩니다. //상태코드 + 구체적인 에러 메세지 errArea.html(jqXHR.status + ", " + jqXHR.responseText); }); //이 propForm은 jquery type let propForm = $("#propForm").on("submit", function(event){ event.preventDefault(); //이 중간에서 비동기요청 처리.. //이 form은 htmlElement type이고 let form = this; $.ajax({ url : form.action, method : form.method, //querystring인데 method 때문에 queryString이 body에 들어감 data : propForm.serialize(), dataType : "json", success : function(resp) { let newProp = resp.model; let option = $("<option>").text(newProp.propertyName); propSel.prepend(option); form.reset(); }, }); return false; }); let dataArea = $("#dataArea"); let propSel = $("#propSel").on("change", function(){ let propertyName = $(this).val(); //여기서 다시 비동기 요청이 발생해야 함 $.ajax({ url : "<%=request.getContextPath() %>/property", data : { name : propertyName }, dataType : "json", success : function(resp) { //여기서 뭘 꺼내야 할까요? model을 꺼냈죠 아래에서는? let propertyVO = resp.model; let trTag = $("<tr>").append( $("<td>").html(propertyVO.propertyName) ,$("<td>").html(propertyVO.propertyValue) ,$("<td>").html(propertyVO.description) ); dataArea.html(trTag); }, }); }); $.ajax({ url : "<%=request.getContextPath() %>/properties", success : function(resp) { let dataList = resp.model; let options =[]; $.each(dataList, function(index, propertyVO){ let option = $("<option>").text(propertyVO.propertyName); options.push(option); }); propSel.append(options); }, }); }); </script> </body> </html> <!-- // 다음의 모든 요청은 비동기 처리를 기반으로 함. // 1. 현재 페이지가 랜더링된 후 전체 프로퍼티 정보를 조회하여 select option 을 완성함. line : /properties (GET) // 2. 선택 option이 변경될때마다 해당 프로퍼티의 정보를 조회하여 dataArea 에 랜더링함. line : /property?name=prop1 (GET) // 3. 하단 form 을 비동기로 전송하여 새 프로퍼티를 추가하고, 기존의 option들의 앞메 추가함. line : /property (POST) // 4. 모든 message(content)는 "json" 형식으로 교환됨. // 5. 요청 처리에 실패한 경우, 해당 상태코드와 응답 메시지를 errArea 에 랜더링함. -->
propertyMng.jsp
---
뭔가 안되져?중복되는거 지우자Factory Method Pattern을 써보자..???????
-----------------------------------
* Design Pattern
* 크게 세 종류가 있음
* 1. (객체)생성 패턴 : Singleton Pattern, * Design Pattern
* 크게 세 종류가 있음
* 1. (객체)생성 패턴 : Singleton Pattern, Factory Method Pattern, Builder Pattern
* 2. (객체 사이의 관계 설정하는) 구조 패턴 : Adapter[Wrapper] Pattern, Proxy Pattern
* 3. (문제 해결 과정을 어떻게 캡슐화 할 것인지 표현하는) 행위 패턴 : Template Method Pattern, Command Pattern, Strategy Pattern
* , Builder Pattern
* 2. (객체 사이의 관계 설정하는) 구조 패턴 : Adapter[Wrapper] Pattern, Proxy Pattern
* 3. (문제 해결 과정을 어떻게 캡슐화 할 것인지 표현하는) 행위 패턴 : Template Method Pattern, Command Pattern, Strategy Pattern
*--------------------------------------------
Factory Method Pattern - 객체 생성만을 전담함.
- 우리가 만든 ConnectionFactory는, connection 객체만 생성함
persistant와 db 사이에 발생하는 결합력은 ConnectionFactory가 가져감.
db종류가 바뀌거나 연결 종류가 바뀌면
package kr.or.ddit.db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /** * * Design Pattern * 크게 세 종류가 있음 * 1. (객체)생성 패턴 : Singleton Pattern, Factory Method Pattern, Builder Pattern * 2. (객체 사이의 관계 설정하는) 구조 패턴 : Adapter[Wrapper] Pattern, Proxy Pattern * 3. (문제 해결 과정을 어떻게 캡슐화 할 것인지 표현하는) 행위 패턴 : Template Method Pattern, Command Pattern, Strategy Pattern * * */ public class ConnectionFactory { static { try { Class.forName("oracle.jdbc.driver.OracleDriver"); }catch(ClassNotFoundException e) { throw new RuntimeException(e); } } public static Connection getConnection() throws SQLException { String oracleURL = "jdbc:oracle:thin:@//localhost:1521/XE"; String oracleUser = "ddit"; String oraclePassword = "java"; return DriverManager.getConnection(oracleURL, oracleUser, oraclePassword); } }
어쨋든 수정을 해 줘야 함...
그러면 결합력을 아예 없애려면
db종류 바뀌면 여길 연결종류, 설정 바뀌면 여길 지워버려야 함. 그러면 이 java code는 손 댈 필요가 없겠쬬ㅕ.
외부로 빼버립시다.
properties 파일을 만들어 줍니다.
이렇게 DB정보를 properties 파일에 적어주면 이렇게 지울 수 있겠죠 이제 static block에서 properties 파일을 읽어와야겠죠
입력 스트림이 필요함
클래스 로더나 클래스가 필요..
package kr.or.ddit.db; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * * Design Pattern * 크게 세 종류가 있음 * 1. (객체)생성 패턴 : Singleton Pattern, Factory Method Pattern, Builder Pattern * 2. (객체 사이의 관계 설정하는) 구조 패턴 : Adapter[Wrapper] Pattern, Proxy Pattern * 3. (문제 해결 과정을 어떻게 캡슐화 할 것인지 표현하는) 행위 패턴 : Template Method Pattern, Command Pattern, Strategy Pattern * * */ public class ConnectionFactory { //전역변수로 쓰고 static으로 줍니다 private static String oracleURL; private static String oracleUser; private static String oraclePassword; static { Properties dbInfo = new Properties(); try ( InputStream is = ConnectionFactory.class.getResourceAsStream("/kr/or/ddit/db/DBInfo.properties"); ){ //파일에 있던 데이터가 메모리에 로드 됨. 이제 모든 데이터는 dbInfo를 통해 처리 가능 dbInfo.load(is); oracleURL = dbInfo.getProperty("url"); oracleUser = dbInfo.getProperty("user"); oraclePassword = dbInfo.getProperty("password"); Class.forName(dbInfo.getProperty("driverClassName")); }catch(ClassNotFoundException | IOException e) { throw new RuntimeException(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(oracleURL, oracleUser, oraclePassword); } }
이제 어디에도 주소나 db정보 이런거 없음. 수정할 필요 없음. 결합력이 한단계 더 낮아짐
다 쪼갰더니 DataBasePropertyDAOImpl 코드가 한결 깔끔해졌지만 맘에들지 않음 ibatis가 자꾸 생각남
그러면 한두줄이면 끝나니깐...
test 를 해볼거예요
이 메서드만 package kr.or.ddit.props.dao; import static org.junit.Assert.assertNull; import org.junit.Test; import kr.or.ddit.props.vo.PropertyVO; public class DataBasePropertyDAOImplTest { PropertyDAO dao = new DataBasePropertyDAOImpl(); @Test public void testSelectProperty() { PropertyVO vo = dao.selectProperty("1' or '1'='1"); assertNull(vo); } }
이릏게 해서 테스트 실행해보자
테스트 실패함 왜지? sys out 으로 검토해보자
멀쩡한 데이터가 나옴 뭔가 문제가 있음
무슨 공격기법떄문이라고
제일 먼저 막아야 하는 해킹기법...????
이름이 뭐랃노
[임] [오후 5:46] oraclePassword
[임] [오후 5:46] sql injection이라는 공격기법이고
KISA 한국인터넷진흥원
www.kisa.or.kr
https://www.kisa.or.kr/2060204/form?postSeq=5&page=1
KISA 한국인터넷진흥원
www.kisa.or.kr
이걸 다운받아서 pdf 보자
client가 보내는 어떤 데이터도 믿으면 안된다 검증.
이유가
이런 공격기법들 때문임
코드예제가있는데
이런 코드가 주로 당한다고 그래서 이렇게 PreparedStatement 사용함. sql injection에서도 안전한 코드라고 함.
ibatis에서도 동일하게 사용되는 내용이었음. 잘 기억하자.
----
DB연결 과정에서 Driver가 무슨 역할을 하느냐.
1. 독립성을 보장하기 위함.
: 내가 DB에 종속되지 않기 위해서. 그렇게 되면 나는 모든 DB에 연결, 사용법을 알아야 하는데
interface(java.sql package)를 알면 공통된 조작법만 알면 됨.
<%@page import="java.util.ArrayList"%> <%@page import="kr.or.ddit.props.vo.PropertyVO"%> <%@page import="java.util.List"%> <%@page import="java.sql.DriverManager"%> <%@page import="java.sql.SQLException"%> <%@page import="java.sql.ResultSetMetaData"%> <%@page import="java.sql.ResultSet"%> <%@page import="java.sql.Statement"%> <%@page import="java.sql.Connection"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%! private Connection oracleConn; private Statement oracleStmt; private ResultSet rs; private ResultSetMetaData rsmd; public void init() throws ServletException{ try { Class.forName("oracle.jdbc.driver.OracleDriver"); //드라이버 로딩한거로 커넥션 수립 } catch (ClassNotFoundException e) { e.printStackTrace(); } } %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>10/jdbcDesc.jsp</title> </head> <body> <h4>JDBC(JavaDataBaseConnectivity)</h4> <pre> 1. 드라이버 빌드 패스에 추가(vendor 제공) 2. 드라이버 로딩(어플리케이션 시작 시점에 한번) - 지금 위리가 사용하는 드라이버 버전이 4세대 이후라서 로딩 안해도 실행됨. 3세대 이전 버전이면 에런라 수 있음. 3. Connection 수립 4. 쿼리 객체 Statement PreparedStatement(선컴파일된 쿼리 객체) CallableStatement(프로시져나 함수와 같은 절차 코드 호출에 사용.) 5. 쿼리 실행 ResultSet executeQuery : SELECT int executeUpdate : INSERT/UPDATE/DELETE 6. 쿼리 실행 결과 처리 7. 자원 해제(***) </pre> <% try { Class.forName("oracle.jdbc.driver.OracleDriver"); //드라이버 로딩한거로 커넥션 수립 } catch (ClassNotFoundException e) {} String oracleURL = "jdbc:oracle:thin:@//localhost:1521/XE"; String oracleUser = "ddit"; String oraclePassword = "java"; String sql = "SELECT PROPERTY_NAME, PROPERTY_VALUE, DESCRIPTION FROM DATABASE_PROPERTIES"; List<PropertyVO> dataList = new ArrayList<>(); //아래에서 다 더해줘서 정보를 dataList객체가 다 가지게 됨 String[] headers = null; //지역변수 초기화를 위해 null값 넣어줘야 try( Connection oracleConn = DriverManager.getConnection(oracleURL, oracleUser, oraclePassword); Statement oracleStmt = oracleConn.createStatement(); ) { rs = oracleStmt.executeQuery(sql); rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); headers = new String[count]; //DB는 index가 1부터 가니까 i가 1로 시작 for(int i = 1; i <= count; i++){ //배열은 java기준이라 0부터 시작하니까 i-1 headers[i-1] = rsmd.getColumnName(i); //아래에서 있어질 table header값도 배열로 미리 받아두는게 가능하죠 } //쿼리 실행결과 담고 활용 while(rs.next()){ PropertyVO vo = new PropertyVO(); dataList.add(vo); vo.setPropertyName(rs.getString("PROPERTY_NAME")); vo.setPropertyValue(rs.getString("property_value")); vo.setDescription(rs.getString("DESCRIPTION")); //인덱스 주는 것보다 명확하게 컬럼 이름 주기 //camel표기법, snake표기법 표기법이 있어서 상호간 유추 가능 //reflection 기술만 있으면 이걸 수동으로 안해도 된다는거지 //그걸 활용해놓은게 ibatis라는 녀석? //자바는 대소문자 구분 안 해서 소문자로 PROPERTY_VALUE 적어줘도 됨 } } catch (SQLException e) { e.printStackTrace(); } %> <table border="1"> <thead> <tr> <th><%=headers[0] %></th> <th><%=headers[1] %></th> <th><%=headers[2] %></th> </tr> </thead> <tbody> <% for(PropertyVO vo : dataList) { %> <tr> <td><%=vo.getPropertyName() %></td> <td><%=vo.getPropertyValue() %></td> <td><%=vo.getDescription() %></td> </tr> <% } %> </tbody> </table> </body> </html>
잘못된 코드의 전형?
수정한다그러면 뭘 어디부터 건드려야 할지 감도 안옴.
나중에 layer 구조로 쪼갤 거임.
PropertyVO == Domain Layer : db의 table 구조를 그대로 표현하기 위해서,
PropertyDAO == Persistance Layer 인터페이스만 있음. -> DB가 어느거든 상관하지 않겠다. 실제 구현체에 종속되지 않는 코드 짤 수 있음.
PropertyServiceImpl
이걸 걷어내도 문제가 없음.interface 해놔서 이렇게 하는게 나중에 우리가 해야 할 dependency injection 구조....
그러면 우리가 지금 알아야 하는건 저 한 줄이 어떤 문제를 발생 시키는가.
저기에 실 구현체가 종속되는 형태라고 하면, db가 바뀌면, 어떻게든 service 객체를 수정해버려야 하는 상황이 오기 떄문임
이제 만든 걸 client에게 전달해주는 역할 하는 게 (+ 요구사항 검증 하는게)
PropertiesControllerServlet
마지막으로 json
JsonViewServlet
지금 책임별로 코드가 다 쪼개져있죠.
우리는 두가지 형태의 코드를 다 가지고 있죠
미션1.
저 위에 나쁜코드랑 쪼개놓은 코드를 비교해보세요. 그러면 왜 쪼개야 하는지 알 수 있죠.
미션2.
앞으로 오라클을 수업 시간에 많이 가지고 놀 거예요
그런데 그러면 mariaDB를 안 쓰게 될 수도 있쬬
python 써야 할 상황이 올 수도 있잖아요? 그러면 한 application에서 db를 두개를 쓸 수도 있으니까...
WebStudyhomework 로 쪼개놓은 project에서
emp의 전체 사원에 대한 정보를 browser로 출력해 보는 거, model2, layered 구조로 짜보세요
'main' 카테고리의 다른 글
22-09-21_jsp(2) (0) 2022.09.21 22-09-21_jsp(1) (0) 2022.09.21 22-09-20_python (0) 2022.09.20 22-09-19_jsp (2) 2022.09.19 22-09-19_python (0) 2022.09.19