내가 보려고 정리하는/Spring

웹 : 0321

보동이용용 2023. 3. 21. 08:47
반응형

리플렉션이란 객체를 통해 클래스의 정보를 분석해내는 프로그램 기법 Java에서는 Reflection 을 이용해 실행중인 자바 프로그램 내부의 클래스,필드,메서드의 속성을 조회,수정할 수 있습니다.

 

생성 디자인패턴


더보기

<< 복습 >>

Facade Pattern : ex)빔프로젝트와 리모컨의 관계

==> 리모트 조작방법을 알고 그걸이용하여 기계를 조착하는... 그 리모컨이 바로 Driver

리모컨을 누른다에 대한 설명 : java.sql.lang package 이것의 구현체 Driver

사용순서.

1. Driver ->  Build Path(class Path)

JRE System Library 모든 자바 어플리 케이션에서 사용 가능

Server Runtime 모든 웹 어플리케이션에서 사용 가능

2. Loading : 메소드 영역(상수 공간)에 적재한다.

모두가 사용할 수 있게 static 코드블럭에 넣었다.

3. Connection

4.  Query Object(statement)

5. 쿼리문 DCL DML TCL -> executeQuery/ executeUpdate....내용반환/int반환

6. 도메인 레이어를 이용하여 자바 객체로 만든다. 이 객체를 POZO로 부른다. 

7. 모든 자원 해제 ...!

 

기본 흐름을 알면 어떤 프레임웍을 만나도 순서에 따라 읽을 수 있기 때문에 쉽게 사용할 수 있다.

여기서 이런 패턴을 코드화 시키는 것이 Lib나 F.W이 된다. => iBatis, myBatis

 

🚩오늘의 수업

1. 어플리케이션이 효과적으로 동작하도록 : 퍼포먼스 체크

>> 어떻게 어플리케이션을 만들었을때 그 퍼포먼스를 올릴 수 있는지. >> Object Pooling 사용해보자.

2. CRUD를 jdbc로 만들어보자. 그다음에 프레임 웍을 사용할 것이다.

3. 어느 단계에서 프레임 웍이 동작하는지 알아보자.

 


⛑️Command Object

검증이 필요한 사용자가 입력한 입력값, 데이터베이스에서 받아와서 도메인 레이어를 거쳐 만들어진 객체 


반복되는 코드 템플릿으로 만들기

▶  TableSchemaVO  ,  TableSchemaDAOImpl

public TableSchemaVO(String tableName, String tableSpaceName, Integer numRows) {
    super();
    this.tableName = tableName;
    this.tableSpaceName = tableSpaceName;
    this.numRows = numRows;
}

어디에서 상용하게 해주려고 기본생성자도 있어야하고

셋터를 사용하기 위해 파라미터를 받는 생성자도 있으면 좋다.

//TableSchemaDAOImpl

@Override
public List<TableSchemaVO> selectTableSchemaList() {
    StringBuffer sql = new StringBuffer();
    // 1. 쿼리 결정
    sql.append(" SELECT TABLE_NAME, TABLESPACE_NAME, NUM_ROWS ");
    sql.append(" FROM USER_TABLES                             ");
    List<TableSchemaVO> list = new ArrayList<>();
    try (
            Connection conn = ConnectionFactory.getConnection();
            PreparedStatement pstmt = conn.prepareStatement(sql.toString());
    ) {
        // 2. 쿼리 파라미터 설정

        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            // 3. 결과집합 -> POJO(VO)
            TableSchemaVO vo = new TableSchemaVO(
                rs.getString("TABLE_NAME")
                , rs.getString("TABLESPACE_NAME")
                , rs.getInt("NUM_ROWS")
            );
            list.add(vo);
        }

        return list;
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}

 

▶TableSchemaControllerServlet

String viewName = "";
req.getRequestDispatcher(viewName).forward(req, resp);

계속 컨트로러에서 반복되는 코드 ==> 이것을 프레임화 한것이 스프링...! 

 

▶schemaView.jsp

let tableTbody = $("#tableTbody");
let makeTR = function(tableSchemaVO){
    return $("<tr>").append(
                $("<td>").html(tableSchemaVO.tableName)
                ,$("<td>").html(tableSchemaVO.tableSpaceName)
                ,$("<td>").html(tableSchemaVO.numRows)
            ).data("source", tableSchemaVO);
}
$.getJSON("<%=request.getContextPath() %>/schema/tableSchema")
    .done(function(resp){
        let trTags = [];
        $(resp.tableList).each(function(idx, tableSchemaVO){
            trTags.push( makeTR(tableSchemaVO) );
        });
        tableTbody.empty();
        tableTbody.append(trTags);
    });

1단계미션

 

$("#tableTbody tr").on("click", function(){
		
	});
	let tableTbody = $("#tableTbody");
	let makeTR = function(tableSchemaVO){

온클릭 작동 안함...

실행 순서가 안맞음 ㅠㅠ

디센던트 구조를 사용하자

그러면 나중에 이벤트 버블링 구조에 따라 tr을 찾게 된다.ㄹㅇㄹ

이제 모달 띄우기1

근데 모달은 띄우는 방식이 따로 있다. onclick아누저도 됨. 다시 지워지워

그리고 모달 창 받아오고, 

return $("<tr data-bs-toggle='modal' data-bs-target='#exampleModal'>").append(
					$("<td>").html(tableSchemaVO.tableName)
					,$("<td>").html(tableSchemaVO.tableSpaceName)
					,$("<td>").html(tableSchemaVO.numRows)
				).data("source", tableSchemaVO);
	}

tr태그에 모달 정보를 넣어 뜨도록 한다.

 

▶\09\propertyView.js

let propTd = event.relatedTarget;
let modifyProp = $(propTd).parents("tr:first").data("source");

테이블 스키마 VO 얻을 수 있는 코드

jquery 홈페이지.

▶ColumnSchemaDAOImpl

// 1. 쿼리 결정
sql.append(" SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE ");
sql.append(" FROM USER_TAB_COLUMNS                     ");
sql.append(" WHERE TABLE_NAME = ?               ");

#{         } >> 인라인 파라미터 , ibtis에서 ?를 더 명확하게 처리하는 방식, 내부적으로 나중에는 ?로 바뀐다.

//	persistence framework (sql mapper, data mapper, orm framework) : iBatis, myBatis, JPA, Hibernate...
	@Override
	public List<ColumnSchemaVO> selectColumnSchemaListByTableName(String tableName) {
		StringBuffer sql = new StringBuffer();
		// 1. 쿼리 결정 : sql mapper
		sql.append(" SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE ");
		sql.append(" FROM USER_TAB_COLUMNS                     ");
		sql.append(" WHERE TABLE_NAME = ?               ");
		List<ColumnSchemaVO> list = new ArrayList<>();
		try (
				Connection conn = ConnectionFactory.getConnection();
				PreparedStatement pstmt = conn.prepareStatement(sql.toString());
		) {
			// 2. 쿼리 파라미터 설정
			pstmt.setString(1, tableName);
			
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				// 3. 결과집합 -> POJO(VO) : data mapper 프레임웤이 동작할때, reflection 방식을 사용함.
				ColumnSchemaVO vo = new ColumnSchemaVO();
				list.add(vo);
				vo.setTableName(rs.getString("TABLE_NAME"));
				vo.setColumnName(rs.getString("COLUMN_NAME"));
				vo.setDataType(rs.getString("DATA_TYPE"));
			}

			return list;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

}

 

▶SchemaServiceImpl

public class SchemaServiceImpl implements SchemaService {
	private TableSchemaDAO tableDAO = new TableSchemaDAOImpl();
	// 의존관계 형성을 위한 코드로 발생하는 강결합을 DI(의존성주입) 구조를 통해 해결해야 함.
	private ColumnSchemaDAO colDAO = new ColumnSchemaDAOImpl();
	
	@Override
	public List<TableSchemaVO> retrieveTableSchemaList() {
		List<TableSchemaVO> list = tableDAO.selectTableSchemaList();
		if(list.isEmpty())
			throw new RuntimeException("테이블이 하나도 없음.");
		return list;
	}

	@Override
	public List<ColumnSchemaVO> retrieveColumnSchemaListByTableName(String tableName) {
		List<ColumnSchemaVO> list = colDAO.selectColumnSchemaListByTableName(tableName);
		if(list.isEmpty())
			throw new RuntimeException(String.format("%s 테이블의 컬럼이 없음", tableName));
		return list;
	}

}

디펜던시 인젝션

 

▶ColumnSchemaControllerServlet

의존성을 만드는 코드가 매번 첫 시작에 생긴다. : 나중에 필터를 이용해 처리해보겠다.

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setCharacterEncoding("UTF-8");

    String tableName = req.getParameter("what");
    if(StringUtils.isBlank(tableName)) {
        resp.sendError(400, "필수 파라미터 누락");
        return;
    }

    List<ColumnSchemaVO> columnList = serivce.retrieveColumnSchemaListByTableName(tableName);
    req.setAttribute("columnList", columnList);
    String viewName = "/jsonView.view";
    req.getRequestDispatcher(viewName).forward(req, resp);
}

일단 지금은 그냥 평소대로 해보자.

▶\schemaView.jsp

let makeTableTR = function(tableSchemaVO){
    return $("<tr data-bs-toggle='modal' data-bs-target='#exampleModal'>").append(
                $("<td>").html(tableSchemaVO.tableName)
                ,$("<td>").html(tableSchemaVO.tableSpaceName)
                ,$("<td>").html(tableSchemaVO.numRows)
            ).data("source", tableSchemaVO);
}

let makeColTR = function(colSchemaVO){
    return $("<tr>").append(
            $("<td>").html(colSchemaVO.tableName)
            ,$("<td>").html(colSchemaVO.columnName)
            ,$("<td>").html(colSchemaVO.dataType)
        ).data("colsource", colSchemaVO);	
}

 

두 코드가 반복된다. 거의 똑같다. 그렇다면 ?

let tableTbody = $("#tableTbody").data("propNames", ["tableName", "tableSpaceName", "numRows"]);
let colTbody = $("#colTbody").data("propNames", ["tableName", "columnName", "dataType", "dataLength", "nullable"]);

코드에 필요한 propNames를 배열로 따로 설정하고, 

let makeCommonTR = function(sourceObj, listTbody){
    let propNames = listTbody.data("propNames");
    let tdTags = [];
    $(propNames).each(function(idx, name){
        tdTags.push( $("<td>").html(sourceObj[name]) );
    })
    return $("<tr>").append(tdTags).data("source", sourceObj);
}

이렇게 합쳐볼 수 있다.

이런식으로 반복되는 코드를 범용적으로 쓸 수 있도록 수정한 것들이 프레임워크같은 것들이다.

그런데 이런 기본적인 흐름을 모른다면 프레임 워크를 봤을 때도 흐름을 몰라서 그냥 기계적으로 쓸 뿐이다. 흐름을 알 수 있도록 기본적인 원리도 이해해보자! 그래야 더 잘 활용하여 쓸 수 있다.

 

	public Integer getDataLength() {
		return dataLength;
	}
	public void setDataLength(Integer dataLength) {
		this.dataLength = dataLength;
	}
	public String getNullable() {
		return nullable;
	}
	public void setNullable(String nullable) {
		this.nullable = nullable;
	}
	public String getTableName() {
		return tableName;
	}
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	public String getColumnName() {
		return columnName;
	}
	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}
	public String getDataType() {
		return dataType;
	}
	public void setDataType(String dataType) {
		this.dataType = dataType;
	}
	@Override
	public String toString() {
		return "ColumnSchemaVO [tableName=" + tableName + ", columnName=" + columnName + ", dataType=" + dataType
				+ ", dataLength=" + dataLength + ", nullable=" + nullable + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((columnName == null) ? 0 : columnName.hashCode());
		result = prime * result + ((tableName == null) ? 0 : tableName.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ColumnSchemaVO other = (ColumnSchemaVO) obj;
		if (columnName == null) {
			if (other.columnName != null)
				return false;
		} else if (!columnName.equals(other.columnName))
			return false;
		if (tableName == null) {
			if (other.tableName != null)
				return false;
		} else if (!tableName.equals(other.tableName))
			return false;
		return true;
	}

 

와.. 길고 귀찮아.

프레임웍 써보자

https://projectlombok.org/

 

Project Lombok

 

projectlombok.org

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.

1. 라이브러리, 2. 플러그인 으로 쓰인다.

이건 플러그인 설치 방법

 

라이브러리는 lib에 복사해넣기

@Getter
@Setter
@ToString(exclude = {"dataType"})
@EqualsAndHashCode(of = {"tableName", "columnName"})
public class ColumnSchemaVO implements Serializable {
	private String tableName;
	private String columnName;
	private String dataType;
	private Integer dataLength;
	private String nullable;

}

 

@Data
@ToString(exclude = {"dataType"})
@EqualsAndHashCode(of = {"tableName", "columnName"})

것두 귀찮으면 이렇게 줄일 수 잇다.

@Data
@EqualsAndHashCode(of = "tableName")
@AllArgsConstructor //모든 인수 다 받는 생성자
@NoArgsConstructor  //기본 생성자
public class TableSchemaVO implements Serializable {
	
	private String tableName;
	private String tableSpaceName;
	private Integer numRows;
	
	
	
}

 

 

오늘 보강시간 : iBatis가 어떻게 sql문이랑 셋터를 가져갔는지 알아보자.

 

그런데 롬복을 사용할 수 있는 환경이 있고 아닌 경우도 있다. 

그렇기 때문에 자바 빈 규약을 잘 알아야한다.

/** * JavaBean 규약
* ValueObject, DataTrendferOobject, Model, Bean
* ex) MemberVO, MemberDTO, MemberModel, MemberBean
*
* 1. 값을 저장할 속성(property) 정의
* 2. 캡슐화
* 3. 캡슐화된 데이터에 접근할 인터페이스 제공(getter/setter)
* get[set]프로퍼티명에서 첫문자만 대문자, camel case 적용
* 4. 상태 비교 메소드 제공
* 5. 상태 확인 메소드 제공
* 6. 직렬화 가능
*
* 회원관리와 인증시스템을 위한 Domain Layer
*/

 

점층적 생성자 패턴 : 점층적으로 파라미터의 갯수가 늘어난다...

만약 파라미터가 30개가 넘어가면? 어려움이 생긴다.

▶ColumnSchemaDAOImpl 

자바 빈 패턴

그런데 적어도 두번은 나눠서 저장을 해야한다.

>> 이것을 해결한 것이 디자인 패턴

 


package kr.or.ddit.designpattern.builderpattern;

TableSchemaVO

빌더 패턴 만드는 방법 순서!

package kr.or.ddit.designpattern.builderpattern;

import java.io.Serializable;

public class TableSchemaVO implements Serializable {
	
	//1. 생성자를 만드는데, 2. private으로 만든다.
	private TableSchemaVO(String tableName, String tableSpaceName, Integer numRows) {
		super();
		this.tableName = tableName;
		this.tableSpaceName = tableSpaceName;
		this.numRows = numRows;
	}

	private String tableName;
	private String tableSpaceName;
	private Integer numRows;
	
	
	public String getTableName() {
		return tableName;
	}

	public String getTableSpaceName() {
		return tableSpaceName;
	}

	public Integer getNumRows() {
		return numRows;
	}

	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((tableName == null) ? 0 : tableName.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		TableSchemaVO other = (TableSchemaVO) obj;
		if (tableName == null) {
			if (other.tableName != null)
				return false;
		} else if (!tableName.equals(other.tableName))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "TableSchemaVO [tableName=" + tableName + ", tableSpaceName=" + tableSpaceName + ", numRows=" + numRows
				+ "]";
	}
	
	public static TableSchemaVOBuilder builder() { //3. 아무나 만들 수 없도록 static으로 빌더메서드를 만든다.
		return new TableSchemaVOBuilder();
	}
	
	//4. 이너클래스
	public static class TableSchemaVOBuilder{
		//5. 빌더는 빌드할 객체가 가지고 있는 모든 프로퍼티를 똑같이 갖고 있어야한다.
		private String tableName;
		private String tableSpaceName;
		private Integer numRows;
		
		//6. 프로퍼티와 이름이 똑같은 메서드가 있어야한다.
		public TableSchemaVOBuilder tableName(String tableName) {
			this.tableName = tableName;
			return this;
		}
		
		public TableSchemaVOBuilder tableSpaceName(String tableSpaceName){
			this.tableSpaceName = tableSpaceName;
			return this;//메소드의 체인구조 제이쿼리 함수의 체인구조처럼
		}
		
		public TableSchemaVOBuilder numRows(Integer numRows){
			this.numRows = numRows;
			return this;
		}
		
		public TableSchemaVO build() { //7. 빌드라는 메서드 있어야한다.
			return new TableSchemaVO(tableName, tableSpaceName, numRows);
		}
	}
	
	
	
}

프레임웍 안에서 포조를 사용하려면 기본 생성자가 있어야한다. ㅇ

 

SLF4J

Simple Logging Facade : 그럼 누가 리모컨 되어지는거지? 그건바로 로그포제이!

 

미션 : 퍼사드 패턴 구현해보기

 

💊💊jquery 플러그인 (페이징 등등)

https://datatables.net/

 

반응형