본문 바로가기
Spring/spring

스프링 3일차 정리

by dyddyd0 2024. 7. 11.

오늘은 데이터베이스 연결 객체와
AOP설정 aop:befor aop:after aop:around를 이용한 기능코드 호출(실행),
JDBC(JDBCTemplate를 사용하는 두 가지 방법

(JdbcDaoSupport클래스를 상속받아 사용, JdbcTemplate를 필드변수로 선언해 사용))

을 공부했다.

 


DB연결 객체

 

@Repository
public class NoticeBoardDao {
    private Connection conn = null;
    private PreparedStatement stmt = null;
    private ResultSet rs = null;
...

 

Connection : DB 연결을 담당하는 객체

`conn = JDBCUtil.*getConnection*();` 

Statement 객체를 생성할 수 있게 JDBC연결을 가져옴

 

 

PreparedStatement : SQL문 실행 담당하는 객체

stmt = conn.prepareStatement(DELETE);

미리 정의해 둔 DELETE 쿼리를 담아서 저장하고

stmt.setInt(1, id);

stmt.executeUpdate();

? 에 넘겨줄 값을 넣어서

실행시킴

private final String DELETE = "DLELTE FROM FreeBoard WHERE ID = ?";

 

 

ResultSet : 쿼리 실행 결과를 담는? 담기 위한? 담을? 객체

    public List<BoardDto> getBoardList(){
        System.out.println("NoticeBoardDao - getBoardList 메서드 호출");
        List<BoardDto> boardDtolist = new ArrayList<>();

        try{
            conn = JDBCUtil.getConnection();

            stmt = conn.prepareStatement(GET_NOTICE_LIST);

            rs = stmt.executeQuery();

            while (rs.next()){
            ...

 

ResultSet rs에 ← stmt안에 담은 sql쿼리를 executeQuery() 쿼리 실행 결과 저장

 

 

—NoticeBoardDao.java—

package com.bit.springboard.dao;

import com.bit.springboard.common.JDBCUtil;
import com.bit.springboard.dto.BoardDto;
import org.springframework.stereotype.Repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

@Repository
public class NoticeBoardDao {
    private Connection conn = null;
    private PreparedStatement stmt = null;
    private ResultSet rs = null;

    // NoticeBoard 게시글 작성하기
    private final String POST = "INSERT INTO Notice(TITLE, CONTENT, WRITER_ID) VALUES(?,?,?)";

    // NoticeBoard 게시글 수정하기
    private final String MODIFY = "UPDATE Notice SET TITLE = ?, CONTENT = ?, MODDATE = ? WHERE ID = ?";

    // NoticeBoard 게시글 삭제
    private final String DELETE = "DELETE FROM Notice WHERE ID = ?";

    // 게시글 전체 불러오기
    private final String GET_NOTICE_LIST = "SELECT Notice.ID, Notice.TITLE, Notice.CONTENT,Notice.WRITER_ID, member.NICKNAME, Notice.REGDATE, Notice.MODDATE, Notice.CNT " +
            "FROM Notice " +
            "join member " +
            "on Notice.WRITER_ID = member.ID";

    // 게시글 하나 찝어서 보기
    private final String GET_ONE_NOTICE = "SELECT" +
                                        " notice.ID," +
                                        " notice.TITLE," +
                                        " notice.CONTENT," +
                                        "notice.WRITER_ID," +
                                        " member.NICKNAME," +
                                        " notice.REGDATE," +
                                        " notice.MODDATE," +
                                        " notice.CNT" +
                                        "   FROM notice" +
                                        "   join member" +
                                        "   on notice.WRITER_ID = member.ID" +
                                        "   WHERE notice.id = ?";

//************************************************************

    public void noticePost(BoardDto boardDto){
        System.out.println("NoticeBoardDao post 메서드 실행");

        try {
            conn = JDBCUtil.getConnection();

            stmt = conn.prepareStatement(POST);

            stmt.setString(1, boardDto.getTitle());
            stmt.setString(2, boardDto.getContent());
            stmt.setInt(3, boardDto.getWRITER_ID());

            stmt.executeUpdate();

        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            JDBCUtil.close(conn, stmt);
        }
        System.out.println("NoticeBoardDao post 메서드 종료");
    }

    public void noticeModify(BoardDto boardDto){
        System.out.println("NoticeBoadrDao - modify 메서드 호출");
        try {
            conn = JDBCUtil.getConnection();

            stmt = conn.prepareStatement(MODIFY);

            stmt.setString(1, boardDto.getTitle());
            stmt.setString(2, boardDto.getContent());
            stmt.setString(3, boardDto.getModdate().toString());
            stmt.setInt(4, boardDto.getId());

            stmt.executeUpdate();

        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            JDBCUtil.close(conn, stmt);
        }
        System.out.println("NoticeBoadrDao - modify 메서드 종료");
    }

    public void delete(int id){
        System.out.println("NoticeBoardDao - delete 메서드 호출");
        try {
            conn = JDBCUtil.getConnection();

            stmt = conn.prepareStatement(DELETE);

            stmt.setInt(1,id);
            stmt.executeUpdate();

        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            JDBCUtil.close(conn, stmt);
        }
        System.out.println("NoticeBoardDao - delete 메서드 종료");
    }

    public List<BoardDto> getBoardList(){
        System.out.println("NoticeBoardDao - getBoardList 메서드 호출");
        List<BoardDto> boardDtolist = new ArrayList<>();

        try{
            conn = JDBCUtil.getConnection();

            stmt = conn.prepareStatement(GET_NOTICE_LIST);

            rs = stmt.executeQuery();

            while (rs.next()){
                BoardDto boardDto = new BoardDto();
                boardDto.setId(rs.getInt("ID"));
                boardDto.setTitle(rs.getString("TITLE"));
                boardDto.setContent(rs.getString("CONTENT"));
                boardDto.setWRITER_ID(rs.getInt("WRITER_ID"));
                boardDto.setNickname(rs.getString("NICKNAME"));
                boardDto.setRegdate(rs.getTimestamp("REGDATE").toInstant().atZone(ZoneId.of("UTC")).toLocalDateTime());
                boardDto.setModdate(rs.getTimestamp("MODDATE").toInstant().atZone(ZoneId.of("UTC")).toLocalDateTime());
                boardDto.setCnt(rs.getInt("CNT"));

                boardDtolist.add(boardDto);
            }

        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            JDBCUtil.close(conn, stmt, rs);
        }

        System.out.println("NoticeBoardDao - getBoardList 메서드 종료");
        return boardDtolist;
    }

    public BoardDto getBoard(int id){
        System.out.println("NoticeBoardDao - getBoard 메서드 실행");
        BoardDto boardDto = new BoardDto();

        try {
            conn = JDBCUtil.getConnection();

            stmt = conn.prepareStatement(GET_ONE_NOTICE);
            stmt.setInt(1, id);

            rs = stmt.executeQuery();

            if (rs.next()){
                boardDto.setId(rs.getInt("ID"));
                boardDto.setTitle(rs.getString("TITLE"));
                boardDto.setContent(rs.getString("CONTENT"));
                boardDto.setWRITER_ID(rs.getInt("WRITER_ID"));
                boardDto.setNickname(rs.getString("NICKNAME"));
                boardDto.setRegdate(rs.getTimestamp("REGDATE").toLocalDateTime());
                boardDto.setModdate(rs.getTimestamp("MODDATE").toLocalDateTime());
                boardDto.setCnt(rs.getInt("CNT"));
            }

        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            JDBCUtil.close(conn, stmt, rs);
        }
        System.out.println("NoticeBoardDao - getBoard 메서드 종료");
        return boardDto;
    }

}

List <Board> getBoardList() 메서드의

boardDto.setRegdate(rs.getTimestamp("REGDATE").toInstant().atZone(ZoneId.of("UTC")).toLocalDateTime()); boardDto.setModdate(rs.getTimestamp("MODDATE").toInstant().atZone(ZoneId.of("UTC")).toLocalDateTime());

UTC로 지정해 주어 시간이 정확하게 나타남.

 

반면, BoardDto getBoard(int id) 메서드의

boardDto.setRegdate(rs.getTimestamp("REGDATE").toLocalDateTime()); boardDto.setModdate(rs.getTimestamp("MODDATE").toLocalDateTime());

시차가 존재한다.

-->

getBoardList 메서드 - UTC지정 해 주어 시차없음

 

 


DB 연동 방식(JDBC/DBCP/JNDI)

 

1. JDBC(Java DataBase Connectivity):

자바에서 제공해 주는 DB 연결 표준 API.

DB 연결 요청이 올 때마다 Web Application이 커넥션을 계속해서 생성하는 방식.

많은 사용자가 동시에 DB 접속을 요청하면, 커넥션의 개수가 증가하여 DB 서버 과부하나 메모리 부족을 야기할 수 있다. 비효율적인 방식이어서 현재는 많이 사용되고 있지 않다.

JDBC 연결

 

 

2. DBCP(DataBase Connection Pool):

Web Application에서 지정한 개수만큼의 DB Connection을 미리 만들어놓고, DB 접속 요청이 올 때마다

만들어놓은 DB Connection을 대여하는 방식.

초기 Connection 개수, 최대 Connection 개수, 평소에 유지되는 Connection 개수 등을 설정할 수 있다.

메모리 낭비가 되지 않도록 적절한 개수를 설정하는 것이 중요하다.

DBCP 연결

 

 

3. JNDI(Java Naming and Directory Interface):

방식은 DBCP와 같지만 DB Connection의 관리주체가 Web Application이 아닌 WAS(Web Application Server)로 변경.

설정이 간편하고 Web Application에 과부하를 방지할 수 있어서 가장 많이 사용되는 방식.

JNDI 연결

 


AOP(Aspect Oriented Programming) 관점지향 프로그래밍!

공통부분을 묶어서 한 번에 처리!

 

 

로그콘솔 클래스 추가.

package com.bit.springboard.service.impl;

import com.bit.springboard.common.LogConsole;
import com.bit.springboard.dao.NoticeBoardDao;
import com.bit.springboard.dto.BoardDto;
import com.bit.springboard.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class NoticeBoardServiceImpl implements BoardService {
    private NoticeBoardDao noticeBoardDao;
    private LogConsole logConsole;

    @Autowired
    public NoticeBoardServiceImpl(NoticeBoardDao noticeBoardDao) {
        this.noticeBoardDao = noticeBoardDao;
        this.logConsole = new LogConsole();
    }

    @Override
    public void post(BoardDto boardDto) {
        logConsole.consoleLog();
        noticeBoardDao.noticePost(boardDto);
    }

    @Override
    public void modify(BoardDto boardDto) {
        logConsole.consoleLog();
        noticeBoardDao.noticeModify(boardDto);
    }

    @Override
    public void delete(int id) {
        logConsole.consoleLog();
        noticeBoardDao.delete(id);
    }

    @Override
    public List<BoardDto> getBoardList() {
        logConsole.consoleLog();
        return noticeBoardDao.getBoardList();
    }

    @Override
    public BoardDto getBoard(int id) {
        logConsole.consoleLog();
        return noticeBoardDao.getBoard(id);
    }
}

로그콘솔 클래스가 → 로그콘솔 V2 클래스로 업데이트된 상황.

= > 다음과 같이 모두 교체해야 됨

 

 

—NoticeBoardServiceImpl 클래스 전체 코드—

package com.bit.springboard.service.impl;

import com.bit.springboard.common.LogConsoleV2;
import com.bit.springboard.dao.NoticeBoardDao;
import com.bit.springboard.dto.BoardDto;
import com.bit.springboard.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class NoticeBoardServiceImpl implements BoardService {
    private NoticeBoardDao noticeBoardDao;
    private LogConsoleV2 logConsole;

    @Autowired
    public NoticeBoardServiceImpl(NoticeBoardDao noticeBoardDao) {
        this.noticeBoardDao = noticeBoardDao;
        this.logConsole = new LogConsoleV2();
    }

    @Override
    public void post(BoardDto boardDto) {
        logConsole.consoleLogPlus();
        noticeBoardDao.noticePost(boardDto);
    }

    @Override
    public void modify(BoardDto boardDto) {
        logConsole.consoleLogPlus();
        noticeBoardDao.noticeModify(boardDto);
    }

    @Override
    public void delete(int id) {
        logConsole.consoleLogPlus();
        noticeBoardDao.delete(id);
    }

    @Override
    public List<BoardDto> getBoardList() {
        logConsole.consoleLogPlus();
        return noticeBoardDao.getBoardList();
    }

    @Override
    public BoardDto getBoard(int id) {
        logConsole.consoleLogPlus();
        return noticeBoardDao.getBoard(id);
    }
}

 

 

root-context.xml 에 aop 설정 추가해 주고.

<beans 
				...
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="  ...
			 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

...
...


<!--AOP설정-->
<!--공통적으로 사용될 클래스를 bean 으로 등록-->
    <bean id="logConsole" class="com.bit.springboard.common.LogConsoleV2"/>
<!--aop:config AOP 의 설정의 루트 엘리먼트.-->
    <aop:config>
<!--        aop:pointcut 공통적인 기능이 실행될 클래스와 메서드를 지정한다.-->
        <aop:pointcut id="allpointcut" expression="execution(* com.bit.springboard.service..*Impl.*(..))"/>
<!--                                   리턴타입과 관계없이 모든 메서드에서 실행할것을 명시 service 패키지의 Impl로 끝나는모든 클래스의 매개변수 관계없이.-->
<!--        aop:aspect 공통 기능의 메소드와 실행될 메서드를 매핑하는 작업-->
        <aop:aspect ref="logConsole">
            <!--before 로 지정하면 메서드 실행 이전에 먼저 method="" 로 지정한 메서드 실행 , 위에 지정한 pointcut 매핑-->
            <aop:before method="consoleLogPlus" pointcut-ref="allpointcut"/>
        </aop:aspect>
    </aop:config>




</beans>

 

추가 정리: 

더보기
포인트 컷 (pointcut) - 
첫 번째 파라미터 : (* or void or !void) 리턴타입을 지정
두 번째 파라미터 : 메소드가 존재하는 클래스나 패키지를 지정
세 번째 파라미터 : 
(*:모든메서드 or
 get*:get으로 시작하는 메서드 or
  (..):매개변수 타입, 개수 제약없음 or
   (*):타입 제약 없지만, 1개의 매개변수 or
    (com.bit.springboard.dto.BoardDTO):매개변수는 하나이고, 매개변수가BoardDTO인 or
     (!com.bit.springboard.dto.BoardDTO):매개변수는 하나이고, 매개변수가BoardDTO가 아닌 or
      (Integer, ..):매개변수 타입과 개수에 제약없지만 첫 매개변수는 Int형 ) 메소드를 지정

<aop:pointcut id="allpointcut" expression="execution(* com.bit.springboard.service..*Impl.*(..))"/>

(*:모든리턴타입 com.bit.springboard.service..*Impl:임플로끝나는모든클래스.*:모든메서드명(..:매개변수타입과 개수 제한없이 모두))

 

aop 동작 시점:

before: 포인트컷 메소드가 실행되기 전에 공통 기능 코드를 실행.
after-returning: 포인트컷 메소드가 정상적으로 종료되고 공통 기능 코드를 실행.
after-throwing: 포인트컷 메소드가 에러를 발생시켰을 때 공통 기능 코드를 실행.
after: 포인트컷 메소드가 정상 종료되거나 에러를 발생시키거나와 상관없이 메소드가 종료되면 무조건 공통 기능 코드 실행.

<- JoinPoint 인터페이스 사용
ProceedingJoinPoint 인터페이스 사용 ->
around: 포인트컷 메소드 실행 전 후로 한 번씩 공통 기능 코드 실행.

 

어드바이스:
공통 관심에 해당되는 공통 기능 코드(consoleLog, consoleLogPlus).
어드바이스를 지정할 때는 공통 기능 코드가 실행될 시점과 함께 지정.

위빙:
            <aop:before method="consoleLogPlus" pointcut-ref="allpointcut"/>
자동으로 consolLogPlus 를 주입해주는 행위

애즈팩트:
어드바이스 + 포인트 컷.
포인트 컷에 위빙이 일어나서 공통 기능 코드가 포인트 컷에 주입된 상태를 애즈팩트라고 한다.

어드바이저:
애즈팩트와 동일.
공통 기능 코드의 메소드 명을 모를 때나 공통 기능이 들어있는 클래스 명을 모를 때 애즈팩트 대신 어드바이저를 사용한다.

트랜잭션을 설정할 때 commit, rollback 시점을 개발자가 알 수 없기 때문에
이 때도 애즈팩트 대신 어드바이저를 사용해서 자동적으로 commit, rollback이 일어나도록 설정한다.

 

 

—작성 후 변경한 NoticeBoardImpl 클래스 전체 코드—

(위에서 작성한 logconsole 변수와 메서드 호출 지워주기 - aop설정을 해두어 자동 호출될 것임)

package com.bit.springboard.service.impl;

import com.bit.springboard.dao.NoticeBoardDao;
import com.bit.springboard.dto.BoardDto;
import com.bit.springboard.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class NoticeBoardServiceImpl implements BoardService {
    private NoticeBoardDao noticeBoardDao;

    @Autowired
    public NoticeBoardServiceImpl(NoticeBoardDao noticeBoardDao) {
        this.noticeBoardDao = noticeBoardDao;
    }

    @Override
    public void post(BoardDto boardDto) {
        noticeBoardDao.noticePost(boardDto);
    }

    @Override
    public void modify(BoardDto boardDto) {
        noticeBoardDao.noticeModify(boardDto);
    }

    @Override
    public void delete(int id) {
        noticeBoardDao.delete(id);
    }

    @Override
    public List<BoardDto> getBoardList() {
        return noticeBoardDao.getBoardList();
    }

    @Override
    public BoardDto getBoard(int id) {
        return noticeBoardDao.getBoard(id);
    }
}

 

NoticeBoardServiceRun 클래스 실행 결과 —>

 

더보기
aop:before 실행결과
<aop:aspect ref="logConsole">
    <aop:before method="consoleLogPlus" pointcut-ref="allpointcut"/>
</aop:aspect>

---
aop:after-returning 실행결과
`<!--공통적으로 사용될 클래스를 bean 으로 등록-->
    <bean id="logConsole" class="com.bit.springboard.common.LogConsoleV2"/>
    <bean id="afterReturning" class="com.bit.springboard.common.AfterReturning"/>`

<aop:aspect ref="AfterReturning"> <!—새로 생성한 AfterReturning클래스—>
            <aop:after-returning method="afterReturningLog" pointcut-ref="allpointcut"/>
</aop:aspect>
​


---
aop:after-throwing 실행결과

 @Override
    public void post(BoardDto boardDto) {
        if(boardDto.getId() == 0){
            throw new RuntimeException("id에 0은 입력될 수 없습니다.");
        }
        freeBoardDao.post(boardDto);
    }
    일부러 예외가 발생하도록 수정

 

//설정

<bean id="afterThrowing" class="com.bit.springboard.common.AfterThrowing"/>

--

<aop:aspect ref="afterThrowing">
            <aop:after-throwing method="afterThrowingLog" pointcut-ref="allpointcut"/>
</aop:aspect>


---
aop:after 실행결과

예외에 관계없이 호출한다.
// 설정

<bean id="after" class="com.bit.springboard.common.After"/>

--

<aop:aspect ref=”after">
            <aop:after method="afterLog" pointcut-ref="allpointcut"/>
</aop:aspect>

 

 

aop:around 실행결과 -->

더보기

 

// 설정

<bean id="around" class="com.bit.springboard.common.Around"/>

--

<aop:aspect ref=”around">
            <aop:around method="aroundLog" pointcut-ref="allpointcut"/>
</aop:aspect>

 

 

pom.xml 에서 AspectJ의 aspectjrt → <scope>runtime</scope> 하나 주석처리 해주면 오류 해결된다.

 


JdbcDaoSupport클래스를 상속받아 사용하기, JdbcTemplate를 필드변수로 선언해 사용하기

 

// JDBCDaoSupport 클래스 상속 방식으로 JDBC Template 사용하기
@Repository
public class NoticeBoardDaoJDBCSupport extends JdbcDaoSupport {

	// JDBCDaoSupport 클래스 상속 방식으로 JDBC Template 사용하기
	@Autowired
	public void setSuperDataSource(DataSource dataSource){
	    super.setDataSource(dataSource);
	}
	...
//JDBC Template 을 필드변수로 선언하고 의존성 주입해 사용하기!!
@Repository
public class NoticeBoardDaoFieldVar {
	
	private JdbcTemplate jdbcTemplate;
	
	//JDBC Template 을 필드변수로 선언하고 의존성 주입해 사용하기!!
	@Autowired // 데이터 소스 연결
	public void NoticeDotJDBC(JdbcTemplate jdbcTemplate){
	    this.jdbcTemplate = jdbcTemplate;
	}
...

 

memberDtoList = jdbcTemplate.query(GET_MEMBERS, new MemberRowMapper());

셀렉트 결과들 : List → query로 새 객체에 넣어서 받아왔다

 

 

jdbcTemplate.update(JOIN, memberDto.getUsername(), memberDto.getPassword(), memberDto.getEmail(), memberDto.getNickname(), memberDto.getTel());

업데이트 결과 → update로 처리했다?

 

단순 업데이트(쿼리실행)와 실행결과 반납(셀렉트 처리한 거 어쩌라고 느낌?) 표현의… 차이 query()와 update()

반응형

'Spring > spring' 카테고리의 다른 글

스프링 6일차 정리  (0) 2024.07.17
스프링 5일차 정리  (0) 2024.07.16
스프링 4일차 정리  (0) 2024.07.14
스프링 2일차 정리  (0) 2024.07.10
스프링 1일차 정리  (0) 2024.07.09