초록꼬마의 devlog
article thumbnail

2022.2.9(수)

🌿 framework 소개

🌱 framework 개념

  • 개발자가 보다 편리한 환경에서 개발할 수 있도록 제공하는 뼈대/틀 -> 소프트웨어 개발의 입장에서는 공통으로 사용하는 library/개발도구/interface 등을 의미

🌱 framework 사용의 필요성, 왜 사용하는가?

  • 현재 웹프로그래밍의 규모가 커지고 있음 -> 거대하고 복잡도가 높은 프로젝트를 완성시키기 위해 많은 사람들이 필요함 -> 그 개발자들이 통일성 있게 빠르고 안정적으로 개발하기 위한 도구로 framework가 좋은 성과를 내고 있음 -> 생산성 향상(=비용 절감)에 도움이 됨

🌱 framework의 특징

  • 자유롭게 설계하고 coding(x) framework가 제공하는 guide대로 setting하고 설계하고 codes 작성(o) -> 개발할 수 있는 범위가 정해져있음
  • 개발자를 위한 다양한 도구/플러그인들이 지원됨

🌱 framework의 장점

  • 개발 시간 단축 가능
  • 오류로부터 자유로워질 수 있음

🌱 framework의 단점

  • 너무 framework에 의존하다보면 개발 능력이 떨어져서 framework 없이 개발하는 것이 어려워짐
  • 습득하는 데에 시간이 오래 걸릴 수 있음

🌱 framework의 종류

1. 영속성(persistence):

  • data 관련(CRUD) 기능들을 편하게 작업할 수 있게 하는 framework e.g. MyBatis
  • 자료를 db에 저장하는 과정을 도와주고 자동화하는 매개 software
  • 응용 prgm과 db 사이에서 벌어질 수 있는 개념적 간극 추상화
  • db 가공하는 Java 객체층-데이터 저장하는 db층 사이를 매끄럽게 연결하는 이음매

2. Java: web application에 초점을 맞춰 필요한 요소들을 module화해서 제공해주는 framework

3. 화면 구현: front-end를 보다 쉽게 구현할 수 있게 해주는 틀을 제공하는 framework

4. 기능 및 지원: 특정 기능이나 업무 수행에 도움을 주는 기능을 제공해주는 framework

🌿 dynamic web project 및 MyBatis 세팅

🌱 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--2022.2.9(수) 11h
    이 문서의 형식이 configuration(환경 설정)임을 알려줌 -> configuration 태그가 전체를 감싸고 있음
    DTD = 유효성(태그들이 configuration 태그 안에 존재해도 되는지)을 체크해줌 vs 단, name 속성 값(대/소문자 구분함), 클래스 경로 오타 등은 DTD도 체크하지 못하므로, 내가 처음에 작성 시 신경써서, 차분하게, 오류 없게, 써야 함-->

<configuration>

    <!--setting = MyBatis 구동 시 선언할 설정들을 작성하는 영역-->
    <settings>
        <!--만약에 null로 데이터가 전달되었다면, 빈칸이 아닌 NULL로 인식-->
        <setting name="jdbcTypeForNull" value="NULL" />
    </settings>

    <!--typeAlias = vo/dto 클래스들의 full 클래스명을 단순한 클래스명으로 사용하기 위한 별칭을 등록할 수 있는 영역; 클래스 body 작성은 안 하더라도, 클래스 파일은 만들고 여기에 추가해서 별명 지어두어야 함 vs otherwise 서버 오류남-->
    <typeAliases>
        <typeAlias type="com.kh.mybatis.member.model.vo.Member" alias="member" />
        <typeAlias type="com.kh.mybatis.board.model.vo.Board" alias="board" />
        <typeAlias type="com.kh.mybatis.board.model.vo.Reply" alias="reply" />
    </typeAliases>

    <!--environment = MyBatis에서 연동할 db(와의 접속)정보들을 등록하는 영역; 여러 개의 db 정보 등록 가능 -> default 속성으로 여러 개의 id 중 어떤 db를 기본 db로 사용할 것인지 설정해줘야 함-->
    <environments default="development">
        <environment id="development">
            <!--transactionManager는 JDBC와 MANAGED 둘 중 하나를 선택할 수 있음
                 - JDBC = 트랜잭션을 내가 직접 관리; 수동 commit
                 - MANAGED = 개발자가 트랜잭션에 대해 어떠한 영향도 행사하지 않음; 자동 commit-->
            <transactionManager type="JDBC" />

            <!--dataSource는 POOLED와 UNPOOLED 둘 중 하나를 선택할 수 있음 = ConnectionPool 사용 여부
                ConnectionPool = Connection 객체를 담아둘 수 있는 영역 -> 1번 생성된 Connection 객체를 담아두면 재사용해서 쓸 수 있음 -> POOLED = 사용하겠다 vs UNPOOLED = 안 쓰겠다
                jdbc 사용 시 connection pool 사용의 장점 = 자원 낭비 방지 <- 만들어서(x) pool에 만들어둔 것(o) 줌-->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
                <property name="username" value="MYBATIS" /> <!--내가 db에서 소문자로 썼어도(오늘 아침의 생성 계정 = mybatis) 대문자로 등록됨-->
                <property name="password" value="mybatis" /> <!--비밀번호는 대/소문자 구별함-->
            </dataSource>
        </environment>
    </environments>

    <!--mapper = 실행할 sql문들을 기록해둔 mapper 파일들을 등록하는 영역; 각 mapper.xml 파일 생성 + mapper 태그에 namespace 속성 부여한 뒤, 여기에 추가해야 함-->
    <mappers>
        <mapper resource="/mappers/member-mapper.xml" />
        <mapper resource="/mappers/board-mapper.xml" />
    </mappers>

</configuration>

 

🌱 Template.java

// 2022.2.9(수) 11h40
public class Template {	
	/* 기존 JDBC
	 * public static Connection getConnection() {
	 * 	// driver.properties 파일 읽어들임
	 *	// 해당 db와 접속된 Connection 객체 생성해서 return
	 * }
	 * 
	 * public static void close(JDBC용 객체) {
	 * 	// 전달받은 JDBC용 객체(자원)를 반납시키는 구문
	 * }
	 * 
	 * public static void commit/rollback(Connection 객체) {
	 * 	// 트랜잭션 처리
	 * }
	 */
	
	// MyBatis
	public static SqlSession getSqlSession() {
		// mybatis-config.xml 파일 읽어들임 -> 해당 db와 접속된 SqlSession 객체 생성해서 반환
		SqlSession sqlSession = null;
		
		// SqlSession 객체를 생성하기 위해서는 SqlSessionFactory 객체가 필요 <- SqlSessionFactory 객체 생성을 위해서는 SqlSessionFactoryBuilder 객체가 필요
		String resource = "/mybatis-config.xml"; // 문자열 가장 앞의 / = 모든 source folder의 최상위 폴더들을 의미 -> 이 경우에는 resources, src
		
		try {
			InputStream stream = Resources.getResourceAsStream(resource); // 자원으로부터 통로를 얻어냄 + 외부 자원을 못 찾을 경우에 io exception이 발생할 수 있는 바, 예외 처리함
			
			// 단계1) new SqlSessionFactoryBuilder() -> SqlSessionFactoryBuilder 객체 생성
			// 단계2) .build(stream) -> 통로로부터 mybatis-config.xml 파일을 읽어들여서 sqlSessionFactory 객체 생성
			// 단계3) .openSession(false) -> SqlSession 객체 생성 + 앞으로 트랜잭션 처리 시 자동으로 commit (안)할 것인지 여부를 지정; false = openSession() 기본 값 = 자동 commit을 하지 않겠다 = 개발자가 직접 commit을 하겠다
			sqlSession = new SqlSessionFactoryBuilder().build(stream).openSession(false);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return sqlSession;
	} // getSqlSession() 종료
}

 

 

💻 회원 가입 기능 구현

✔️ MemberServiceImpl.java 내용 설명 및 예시

@Override
public int insertMember(Member m) {
    // 기존 JDBC 방식
    /*
    Connection conn = JDBCTemplate.getConnection();

    int result = new MemberDao().insertMember(conn, m);

    if (result > 0) {
        JDBCTemplate.commit(conn);
    } else {
        JDBCTemplate.rollback(conn);
    }

    JDBCTemplate.close(conn); // 지금까지 학생들의 질문 = Connection 객체를 왜 service 클래스에서 close하는가?

    return result;
    */

    // 앞으로는 MyBatis에서 제공하는 sqlSession을 가지고 함
    SqlSession sqlSession = Template.getSqlSession();

    int result = memberDao.insertMember(sqlSession, m); // insert의 처리 결과 = 0(실패 시) 또는 1(성공 시)

    if (result > 0) {
        sqlSession.commit();
    }
    /*
    else { // else = 'result = 0' = db에 뭔가 들어간 것/insert된 데이터가 없음 -> rollback할 필요 없음
           // 지금까지 수업하며 rollback이 꼭 필요했던 경우 = sql문을 1개 이상 보낼 때 e.g. 첨부파일 upload/insert 실패한 경우, 그에 앞서 게시글 insert한 것 rollback해야 함; 게시글 조회수 count 올려놨는데, 어떤 사유로(악의적인 목적으로 게시글이 망가진? 경우) 게시글 조회 실패한 경우, 게시글 조회수 increaseCount rollback해야 함
        // sqlSession.rollback();
    }
    */

    sqlSession.close();

    return result;
} // 2022.2.9(수) 16h40 insertMember() 종료

 

✔️ MemberDao.java 내용 설명 및 예시

public int insertMember(SqlSession sqlSession, Member m) {
    // 기존 JDBC 방식
    /*
    int result = 0;
    PreparedStatement pstmt = null;
    String sql = prop.getProperty("insertMember");

    try {
        pstmt = conn.prepareStatement(sql);

        pstmt.setString(1, m.getUserId());
        pstmt.setString(2, m.getUserPwd());

        result = pstmt.executeUpdate();
    } catch(xx) {
        예외 처리
    } finally {
        close(pstmt);
    }

    return result;
    */

    // sqlSession.sql문 종류에 맞는 메소드("mapper 파일의 namespace.내가 실행하고자 하는 해당 sql문의 식별자/id", sql문을 완성시킬 객체(해당 sql문이 미완성 상태가 아니라면, sql문을 완성시킬 객체는 생략 가능)) -> sqlSession에서 제공하는 (아주 많은)메소드를 통해서 sql문을 (sqlSession이 알아서)찾아서 실행하고, 결과를 바로 받아볼 수 있음
    return sqlSession.insert("memberMapper.insertMember", m); // insert의 처리 결과 = 0(실패 시) 또는 1(성공 시)
} // 2022.2.9(수) 16h30 insertMember() 종료

 

✔️ member-mapper.xml 내용 설명 및 예시

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--2022.2.9(수) 15h40-->
<mapper namespace="memberMapper">
<!--doctype이 mapper 파일이기 때문에 전체가 mapper 태그 안에 있어야 함
    namespace 속성 = 해당 mapper 파일의 고유한 별칭 -> 이 프로젝트 내에서 중복되면 안 됨-->

<!--다른 mapper.xml 파일에는 여기에 있는 것과 동일한 id 값 가지는 태그가 있을 수도 있음 -> id는 이 xml 파일에서의 고유한 식별자

    DML문일 경우, 쿼리문의 실행 결과 = 처리된 행의 개수 = int형 자료 -> resultType 또는 resultMap 안 써도 됨
    <insert id="각 sql문의 식별자" parameterType="전달받을 Java 타입(full 클래스명) 또는 별칭"(생략 가능)>
        SQL문
    </insert>
    <update></update>
    <delete></delete>
    전달받을 값이 없는 경우, parameterType 속성은 생략 가능

    SELECT문일 경우, db로부터 조회 결과는 ResultSet으로 옴 -> 내가 작성한 sql문에 따라 반환형/조회 결과가 다를 수 있음 e.g. id 찾기 기능 -> String 반환, SELECT COUNT(*) -> int 반환, 로그인 기능 -> Member 객체 반환 등
    <select id="각 sql문의 식별자" parameterType="전달받을 Java 타입(full 클래스명 또는 별칭)"(생략 가능)
            resultType="조회 결과를 반환하고자 하는 Java 타입" 또는 resultMap="조회 결과를 뽑아서 mapping할 resultMap의 id">
        SQL문
    </select>
    전달받을 값이 없는 경우, parameterType 속성은 생략 가능 + resultType(Java에서 제공하는 자료형) 또는 resultMap(내가 만든 vo 클래스 타입)으로 조회 결과 값에 대한 타입을 지정해야 함

    2022.2.10(목) 10h30
    resultMap = MyBatis의 핵심 기능 중 하나 = SELECT문 실행해서 db 조회 결과로 온 ResultSet으로부터 조회된 컬럼 값을 하나씩 뽑아서 내가 지정한 vo 객체의 각 필드에 담을 수 있음
    <resultMap id="식별자" type="조회된 결과를 담아서 반환하고자 하는 vo 객체의 타입(full 클래스명) 또는 별칭">
        <result column="조회 결과를 뽑고자 하는 db 컬럼명" property="해당 결과를 담고자 하는 필드명" />
        <result column="조회 결과를 뽑고자 하는 db 컬럼명" property="해당 결과를 담고자 하는 필드명" />
        ..
    </resultMap>

    ?(위치 홀더) 대신, 해당 sql문에 전달된 객체로부터 값을 꺼내서 #{필드명 또는 변수명 또는 map의 key 값} -> 16h25 나의 질문 = 'map의 key 값'이 무엇이지? >.<
    e.g. 아래 쿼리문 #{userId} = m.getUserId()
                #{userPwd} = m.getUserPwd()-->

    <insert id="insertMember" parameterType="member">
        INSERT INTO MEMBER(USER_NO, USER_ID, USER_PWD, USER_NAME, EMAIL, BIRTHDAY, GENDER, PHONE, ADDRESS)
        VALUES(SEQ_UNO.NEXTVAL, #{userId}, #{userPwd}, #{userName}, #{email}, #{birthday}, #{gender}, #{phone}, #{address})
    </insert>

    <!--2022.2.10(목) 10h30-->
    <resultMap id="memberResultSet" type="member">
        <result column="USER_NO" property="userNo" />
        <result column="USER_ID" property="userId" />
        <result column="USER_PWD" property="userPwd" />
        <result column="USER_NAME" property="userName" />
        <result column="EMAIL" property="email" />
        <result column="BIRTHDAY" property="birthday" />
        <result column="GENDER" property="gender" />
        <result column="PHONE" property="phone" />
        <result column="ADDRESS" property="address" />
        <result column="ENROLL_DATE" property="enrollDate" />
        <result column="MODIFY_DATE" property="modifyDate" />
        <result column="STATUS" property="status" />
    </resultMap>

    <!--2022.2.10(목) 10h25-->
    <select id="loginMember" parameterType="member" resultMap="memberResultSet">
        SELECT USER_NO, USER_ID, USER_PWD, USER_NAME, EMAIL, BIRTHDAY, GENDER, PHONE, ADDRESS, ENROLL_DATE, MODIFY_DATE, STATUS
        FROM MEMBER
        WHERE STATUS = 'Y' AND USER_ID = #{userId} AND USER_PWD = #{userPwd}
    </select>

</mapper>

📗 homework: MyBatis 수업 내용 복습