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 수업 내용 복습
'back-end dev > framework' 카테고리의 다른 글
[KH정보교육원] 90일차_Spring 로그인/아웃 기능 구현, 회원 가입 준비 (0) | 2022.02.17 |
---|---|
[KH정보교육원] 88일차_Spring 세팅 마무리 (0) | 2022.02.15 |
[KH정보교육원] 87일차_Spring 소개, 세팅 (0) | 2022.02.14 |
[KH정보교육원] 86일차_MyBatis 게시판 상세 조회, 검색 기능 구현 (0) | 2022.02.11 |
[KH정보교육원] 85일차_MyBatis 로그인/아웃, 게시판 목록 조회 기능 구현 (0) | 2022.02.10 |