배움 __IL/TIL 1기

TIL : 52번째- 230216 [2-2-목]

Mo_bi!e 2023. 2. 16. 19:23

I. 웹개발

1. 선수과목

기업형의 어플리케이션 구조에 맞추어서 만들자.

 

과거와 달리 이제 Spring이 메인이고, JSP가 옵션이 되었다. 특히 spring boot에서!

jsp 빠지나, 스프링에서 코어가 서블릿임

 

지금까지 JSP배웠고 , 스프링부트에서는 Thymeleaf가 메인이다.

 

Dao 가있으면 Menu 등이있다. update함수가 있고 이 경우 query 문을 실행 함

프레임워크를 이용하면 geter setter 를 자동으로 만들어주고, DB쿼리문을 자동으로 만들어준다

 

2. 컨트롤러

반복하고, 복잡하고, 중복을 잘하고있는 코드이다.

 

(1) 분리하기

1)

지금까지 컨트롤러에는 입출력과 결과물이 상존해 있다. 그러나 이것을 분리할 필요가있다

 

입출력 : View & mdel

결과물 : DB 관련

 

리스트를 달라고하는 코드를 만들자

 

2)

<ListController3.java > : 입출력 관련 

package com.newlecture.web.controller.menu;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.newlecture.web.entity.Menu;
import com.newlecture.web.service.DefaultMenuService;
import com.newlecture.web.service.MenuService;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/menu/list3")
public class ListController3 extends HttpServlet{
	
	private MenuService service;
	
	public ListController3() {
		// TODO Auto-generated constructor stub
		service = new DefaultMenuService();
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		resp.setContentType("text/html; charset=utf-8");
		
		PrintWriter out = resp.getWriter();
		
		
//		GList<Menu> menus = new GList<>(); // 이런 형식의 콜렉션 만들어달라 부탁한 것
		List<Menu> menus = service.getList();
		
		
		
		req.setAttribute("menus2", menus);
		
		req //  list2/list3.jsp
		.getRequestDispatcher("/WEB-INF/view/menu/list3.jsp")
		.forward(req, resp);
		
	}
}

본 클래스에서 MenusService 자료형의 service 인터페이스를 멤버선언해준다

그래고 생성자에서 service인터페이스에다가 구현체인 DefaultMenuService를 넣어준다.

 

3)

<MenuService.java> : DB관련 [Service]

우선 인터페이스를 설정해준다.

package com.newlecture.web.service;

import java.util.List;

import com.newlecture.web.entity.Menu;

public interface MenuService {

	List<Menu> getList();

}

인터페이스를 선언해준다

이 경우에 인터페이스에서 get post put delete 각 상황에 따라서 메소드를 선언해준다.

 

4)

<DefaultMenuService.java>

package com.newlecture.web.service;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.newlecture.web.entity.Menu;

public class DefaultMenuService implements MenuService {

@Override
public List<Menu> getList() {
    // TODO Auto-generated method stub
    List<Menu> menus = new ArrayList<>();

    String query = "";
    String sql = String.format("select * from member where nicname like '%s'", "%"+query+"%") ;

    try {
        // java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
        Class.forName("oracle.jdbc.driver.OracleDriver");
        String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1";
        Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");

        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery(sql);

//			Menu[] menus = new Menu[100];

//			List<Menu> list = new ArrayList<>();



        // 필터링, 집계, 정렬
        while(rs.next())	// 서버의 커서를 한칸 내리고 그 위치의 레코드를 옮겨 오는 것. -> 레코드 하나가 저장되는 공간은?
        {

            int id = rs.getInt("id");
            String name = rs.getString("name");
            String nicName = rs.getString("nicname");
            Date regDate = rs.getDate("reg_date");
            String images = "pic1.png,pic2.png,pic3.png,pic4.png";

            Menu menu = new Menu(id, name, 1000, "",regDate);
            menu.setImages(images);
            System.out.println(menu.toString());

            menus.add(menu);

        }

        con.close();

    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    //리턴은 menus로 !! 
    return menus;
}

}

가장 기본인 get을 본 클래스에서 구현해주었다.

본 클래스에서 getlist() 외에도 추후에  postlist() 등도 구현이 가능하다

결국 CRUD 상황에 따라서 분리했고, 분업이가능하다

 

여기까지만 정리하면 serviceDB관련 CRUD를 서비스업처럼 부지런히 수행한다 

(아래에서는 사용자의 요구임 -> 요구에 따라 데이터 CRUD)

 

서버 코드는 동적인페이지를 만드는 코드이다.

적당한 데이터를 가져다가 적당한 데이터를 

 

- 예전에는 service를 만들고 업무자는 컨트롤러에서 set 하는것을 손댐

- 경험이 없을수록 나누면 힘든데, 이것만 만들어야지 생각하는게 각각의 모듈 만들 때 더 경쾌하게 만들수있다

 

 

(2) Dao 의 이용

1)

사용자가 요구하는 내용이 서비스로 간다

서비스 이름은 "장바구니에 담아주세요" / "좋아요 올라주세요" 이렇게 해야한다

Dao 의 경우에는 그대로 하면안되고, 즉 데이터 요청함수사용자 요청함수같은 이름으로 할 수없다

 

2) <미완>

<DefaultMenuService.java>

package com.newlecture.web.service;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.newlecture.web.entity.Menu;
import com.newlecture.web.repository.MenuDao;


// 다양한 업무를 처리하기 위한 상관 관계를 잘 아는 사람이 데이터를 조작하게끔 한다
// 단 데이터를 조작하기 위한 방법은 몰라도 되도록 하는것이 어떨지...
// 다음과 같은 것들을 모르고도 자바 지식만으로 업무를 처리할수 있게 하는것이 좋지않을까? 

// 어떤 DB를 사용해야 하는지
// 쿼리를 어떻게 작성해야 하는지
// 데이터 소스가 다양한데 그것이 어떤 것들을 사용해야 하는지 ...


// 위와같이 업무를 나누면 다양한 장점이 있다. 
// 데이터 베이스가 달라지면? SQL과 연결 문자열과 드라이브 등이 달라지는데.. 그럼 모든 업무로직코드를 수정해야한다.
		


// 하는게 없어 보일수도 있지만 지금은 업무가 단순해서 그러하다
// 서비스는 업무로직 처리하는 클래스이다.
public class DefaultMenuService implements MenuService {

	private MenuDao menudao;
	
	@Override
	public List<Menu> getList() {
		
		List<Menu> list = menudao.findAll();
        
        		return list;
	}

}

서비스에서 DB와 관련된 부분을 dao로 분리한다

 

<Menudao.java> 인터페이스로 만든다

package com.newlecture.web.repository;

import java.util.List;

import com.newlecture.web.entity.Menu;

public interface MenuDao {

	List<Menu> findAll();

}

 

 

3)

클래스로 한것과 인터페이스로 한것 어떤 차이?

인터페이스 통해서 

s라는 클래스가 Dao 의 B1을 수정하고싶다. 

SOLID 원칙이 녹아있다.

 

--

 

<우선 SOLID 원칙을 알아보자>

S : 모든 객체는 여러 역할을 가지면 안된다. 특화된 하나의 역할을 가짐

즉 객체지향에서 캡슐화란 역할을 나누는 것인데, 두개이상 맡기지 마!

역할분배를 잘 하라는 의미

 

O : 객체가 수정에는 닫혀있고, 확장에는 열려있어야한다.

잘 작동하면 수정하지마! 이미 배포되어서 사용하고 있으니, 확장해서 수정해라

ex : Node JS 를 업데이트 하는데 수정해버림 ( 이미 배포되어서 사용된건데,,,)

 

L : 객체를 참조 할 때 (A형식으로, A형식 참조 뿐만 아니라 부모인 B형식으로도 참조가 가능하다)

A가 B뿐만 아니라 C도 상속할때 문제가 없어야한다.

 

I : 인터페이스가 약속을 정의하는데, 정의하는 목록이 너무 많은 것을 정하지 말라 (S와 유사)

내가 쓰고자 하는 기능이 하나면 자르고 조합해서 하라!

내가 쓰지도 않는 목록 만들어서 합치지 말고, 필요한 내용만큼만 잘게 잘라서 인터페이스를 쓰라!

인터페이스는 다중상속이 가능하니까 통으로 약속하지말고,,,,

 

D: 객체를 사용할 때 사용하는 객체를 A형이라고 할때 그 A형을 쓰지말라

부품객체에 대한 inversion 을 하라 / 가능하면 내가 쓰는 부모형으로 참조해서 써라

-> 추상화에 의존해야지, 구체화에 의존해서는 아니된다 부모나 자식이나 모두 추상화에 의존해야

 

 

 

--

 

4)

테이블에 멤버가 있으면 있으면 Dao에는 memberdao / 테이블에 메뉴이면 menudao / notice이면 noticeDao 이다

테이블에 데이터를 가져와서 자바형태로 사용할수있게 해주는 변환 객체이다.

 

우리는 Dao 를 씀으로서 쿼리를 몰라도 되고, 순수하게 자바로 다룰 수있게 해준다.

Dao라는 클래스가 가진 함수들은 데이터 쿼리를 대신하는것으로 존재함

 

IN, DEL, Up, Find(Sel) 4가지를 대신한다

이런것은 업무적인 이름인가 데이터를 다루는것인가 이는 업무적인 것이 없다

 

<업무적인 것>은 "좋아요를 업 해주세요" 이자 서비스이름이다

좋아요 업은 실제로 구현할 때에는 insert 혹은 add문으로 바뀜

사용자가 원하는 행위 그대로 서비스명이됨

 

앞서 말한것처럼 수정은 닫히게 확장은 열리게 해야하는데

결국 B1은 확장해야함

확장하면 B1 수정안되고 B2로 확장해야한다.

 

그런데 문제가 있음 서비스 S클래스에서 객체 참조하는 B1형식이 존재한다

코드 확장하기 위해서 새 코드 만들었는데, 도미노 현상 발생함

S클래스도 또 새로만들어야하는가? 이를 사용하는 또 호출위해서 또 만들고 도미노이다

도미노를 찾아내야한다.

 

종속성 역전원칙

내가 너무 B1 특화하면은 B2로 바꾸면 같이 수정해야한다.

자식 바꾸어도 나는 문제가 없는(리스코프 치환원칙) S클래스는 구체화된 클래스명으로 쓰면안됨

종속성을 역전해야하기 때문에 치환해도 문제없게끔 해야한다

 

(3) Dao 의 이용 구현하기

1)

그러면 종속성을 역전하기 위해서는 위에 부모를 참조하게끔 하자

 

아래 코드에서

B는 부모(인터페이스)이고 구현된것 B2 이다.

데이터 바꿔치기 해도 인터페이스 기반이기 때문에 어떤객체가 새로 만들어져도 인터페이스만 일치하면 바꿔치기 해도 문제가 없다

B -> B1 에서 B -> B2 로 바꿔치기

 

2)

외부에서 클래스를 생성해주자

 

new라는 연산자로 객체를 생성할수도 있지만, 문자열을 가져오면 문자열 가지고 객체 생성도 가능하다

class.forName 이걸로 내가 생성할 객체를 클래스명으로 외부에 둘 수있다

XML, 다른 프로퍼티 파일이든 가능하다

객체를 생성해서 객체를 넘겨주는게 필요한데, 객체 생성과 조립을 도와주는 도구라고 해서 DI 도구라고한다

DI 는 결국 스프링이다.

 

객체를 생성하는 것을 이 코드에 있으면 어떻게든 수정된다. 객체 생성을 내 코드에서 뗴어내주는 것을 스프링 프레임웍이다.

 

 

3)

 

Dao 를 jdbc와 연결해주기 위해서 repository 패키지에다가 추가해주고, 

<좌측> service 인터페이스와 이를 구현한 하단의 구현체

<우측> Dao 인터페이스와 이를 구현한 하단의 구현체

 

 

4)

지금까지 계층 3개로 나누었는데, 사용자는 모른다.

몇개로 나누든 사용자는 알바없다

어플리케이션은 구조화를 잘해서 몇 계층하던지 상관없다 사용자는 같음

나를위해서! 모듈들을 전문화해서 역할을 나누거나 교체를 쉽게하기 위함이다

 

선택하기위한 옵션으로 긍정적이다라고 하기에는 좋은데, 작은 서비스는 이렇게 할필요가 없다 

그래도 할 것이다

 

만드는것은 두가지 목적이다. 만들기 + 공부하기 이다

기업방식에 맞게 3계층으로 만드는것이 여러모로 도움이 될 것이다.

 

 

 


1. 보충

 

 

 

 

 

2. 회고 

1)

점점 나누는게 흥미롭다.

'배움 __IL > TIL 1기' 카테고리의 다른 글

TIL : 54번째- 230220 [2-3-월]  (0) 2023.02.20
TIL : 53번째- 230217 [2-2-금]  (0) 2023.02.17
TIL : 51번째- 230215 [2-2-수]  (0) 2023.02.15
TIL : 50번째- 230214 [2-2-화]  (0) 2023.02.14
TIL : 49번째- 230213 [2-2-월]  (0) 2023.02.13