오늘은
오늘은 프로그래머 도전기 100일차 포스팅을 하는 날 입니다
매일 블로그를 쓰려고 마음을 먹었었지만
공부를 시작한지 100일이 훨씬 넘었음에도 이제서야 100일차의 포스팅을 하게되었군요
아팠던 날도 있고 쉬었던 날도 있고.. 여러 이유로
몇번이나 빠져버렸군요...
게으름을 피웠던 순간들을 이 기회삼아 반성을 해봅니다...
Spring 공부
<<members project>>
- 스프링(FrameWork) + JSP(화면) + mybatis(DB SQL)
= Spring Legacy (스프링 레거시)
(1. mapper 인터페이스 방식 = 메서드 직접 작성)
2. mapper 클래스 방식 = SqlSessionTemlate 에서 제공되는 메서드 사용해야 함
insert
update
delete
selectOne
selectList
컨텍스트: com.khit.members
의존성 주입 - pom.xml
jdk: 11
spring version: 5.2.7
1. lombok
2. mysql driver
3. spring-jdbc
4. mybatis
5. spring-mybatis
6. juit 4.13
7. spring-test
8. 컨텍스트 실행 -> 톰캣 서버 실행
9. web.xml -> 한글 인코딩 설정
10. root-context.xml -> db설정, context component-scan
11. mybatis Test
12. mapper의 경로 설정 - classpath:/mapper/*.xml
23. 별칭(alias) 설정 - mybatis-config.xml
의존성 주입
* pom.xml
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
한글 인코딩
* web.xml
<!-- 한글 인코딩 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
* root-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- spring-jdbc 설정 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="cohttp://m.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/springdb?serverTime=Asia/Seoul" />
<property name="username" value="springuser" />
<property name="password" value="pwspring" />
</bean>
<!-- mybatis 주요 객체 생성 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- Setter 주입 : dataSource: setDataSource() -->
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:/mapper/*.xml" />
<property name="configLocation" value="classpath:/mybatis-config.xml" />
</bean>
<bean id="sqlSession"
class="org.mybatis.spring.SqlSessionTemplate">
<!-- 생성자 주입 -->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<context:component-scan base-package="com.khit.members" />
</beans>
* 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">
<configuration>
<typeAliases>
<typeAlias type="cohttp://m.khit.members.dto.MemberDTO" alias="member" />
</typeAliases>
</configuration>
controller > service > repository > memberMapper.xml
* MemberController
package com.khit.members.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import cohttp://m.khit.members.dto.MemberDTO;
import com.khit.members.service.MemberService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@AllArgsConstructor // 생성자 주입 어너테이션
@Slf4j
@RequestMapping("/member")
@Controller
public class MemberController {
private MemberService memberService;
}
* MemberService
package com.khit.members.service;
import org.springframework.stereotype.Service;
import cohttp://m.khit.members.dto.MemberDTO;
import com.khit.members.repository.MemberRepository;
import lombok.AllArgsConstructor;
@AllArgsConstructor
@Service
public class MemberService {
private MemberRepository memberRepository;
}
* MemberRepository
package com.khit.members.repository;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import cohttp://m.khit.members.dto.MemberDTO;
import lombok.AllArgsConstructor;
@AllArgsConstructor
@Repository
public class MemberRepository {
private SqlSessionTemplate sql;
}
* memberMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Member">
</mapper>
MemberDTO - id, email, password, name, age
* MemberDTO
package cohttp://m.khit.members.dto;
import lombok.Data;
@Data
public class MemberDTO {
private Long id;
private String email;
private String password;
private String name;
private int age;
}
<회원가입 페이지, 로그인 페이지 생성>
* join.jsp
<body>
<div class="content">
<h2>Join</h2>
<form action="/member/join" method="post">
<p><input type="text" name="email" placeholder="Email"></p>
<p><input type="password" name="password" placeholder="Password"></p>
<p><input type="text" name="name" placeholder="Name"></p>
<p><input type="text" name="age" placeholder="Age"></p>
<p>
<input type="submit" value="MemberJoin">
<input type="reset" value="Cancel">
</p>
</form>
</div>
</body>
* login.jsp
<form action="/member/login" method="post">
<p><input type="text" name="email" placeholder="Email"></p>
<p><input type="password" name="password" placeholder="Password"></p>
<p>
<input type="submit" value="MemberLogin">
<input type="reset" value="Cancel">
</p>
</form>
* memberController
public class MemberController {
private MemberService memberService;
@GetMapping("/join")
public String joinForm() {
return "/member/join";
}
@PostMapping("/join")
public String join(@ModelAttribute MemberDTO memberDTO) {
log.info("MemberDTO = " + memberDTO);
memberService.insert(memberDTO);
return "/member/login";
}
@GetMapping("/login")
public String loginForm() {
return "/member/login";
}
@PostMapping("/login")
public String login(@ModelAttribute MemberDTO memberDTO,
HttpSession session) {
MemberDTO loginMember = memberService.login(memberDTO);
if(loginMember !=null) {
session.setAttribute("sessionEmail", memberDTO.getEmail());
return "/main";
}else {
return "/member/login";
}
}
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate(); // 모든 세션 삭제
return "redirect:/"; // 강제 이동 = redirect
}
}
* MemberService
public class MemberService {
private MemberRepository memberRepository;
public void insert(MemberDTO memberDTO) {
memberRepository.insert(memberDTO);
}
public MemberDTO login(MemberDTO memberDTO) {
return memberRepository.login(memberDTO);
}
}
* MemberRepository
public class MemberRepository {
private SqlSessionTemplate sql;
// insert(삽입), update(변경), delete(삭제)
// selectOne(상세보기), selectList(목록보기)
public void insert(MemberDTO memberDTO) {
sql.insert("Member.insert", memberDTO);
}
public MemberDTO login(MemberDTO memberDTO) {
return sql.selectOne("Member.login", memberDTO);
}
}
* MemberMapper
<mapper namespace="Member">
<insert id="insert">
insert into t_member (email, password, name, age)
value (#{email},#{password},#{name},#{age})
</insert>
<!-- MemberDTO.java = member -->
<select id="login" resultType="member">
select * from t_member where email = #{email} and password=#{password}
</select>
</mapper>
<수정시>
- id로 찾음
- 세션(email)로 찾음
- findByEmail() method 생성
* update.jsp
<p>
<button type="button" onclick="updateFn()">Update</button>
<button type="button" onclick="list()">Cancel</button>
</p>
<script>
// 수정처리
const updateFn = function() {
document.updateform.submit();
/* location.href="/member/update"; */
}
const list = function() {
location.href="/member/";
}
</script>
* memberController
public class MemberController {
// 회원 수정
@GetMapping("/update")
public String updateForm(HttpSession session,
Model model) {
String email = (String)session.getAttribute("sessionEmail");
MemberDTO memberDTO = memberService.findByEmail(email);
model.addAttribute("member", memberDTO);
return "/member/update"; // update.jsp
}
// 회원 수정 처리
@PostMapping("/update")
public String update(@ModelAttribute MemberDTO memberDTO){
memberService.update(memberDTO);
return "redirect:/member?id=" + memberDTO.getId();
}
}
* MemberService
public class MemberService {
public MemberDTO findByEmail(String email) {
return memberRepository.findByEmail(email);
}
public void update(MemberDTO memberDTO) {
memberRepository.update(memberDTO);
}
}
* MemberRepository
public class MemberRepository {
public MemberDTO findByEmail(String email) {
return sql.selectOne("Member.findByEmail", email);
}
public void update(MemberDTO memberDTO) {
sql.update("Member.update", memberDTO);
}
}
* MemberMapper
<mapper namespace="Member">
<select id="findByEmail" resultType="member">
select * from t_member where email = #{email}
</select>
<update id="update">
update t_member set password = #{password}, name = #{name}, age = #{age}
where id = #{id}
</update>
</mapper>
React 공부
<<최적화>>
item을 하나만 삭제해도 나머지 item이 re-rendering 되는 현상 발생
* DiaryItem.js
// DiaryItem component가 prop으로 받고있는 data는 총 7개
const DiaryItem = ({
// App component로 받은 함수 2개
onRemove,
onEdit,
// 나머지 5가지는 data
id,
author,
content,
emotion,
create_date,
// content를 제외한 나머지 data는 변하지 않음
// => onRemove, onEdit, content에 집중해서 최적화를 한다
})
최적화의 시작 = component를 React.memo()로 묶어주기
export default React.memo(DiaryItem);
두번째로 useEffect를 활용하여 어떤 요소들이 re-render되는지 확인
useEffect(() => {
console.log(`${id}번째 아이템 렌더!`);
});
DiaryItem은 React.memo()로 감싼다고 바로 최적화가 되는 component는 아니다
= onEdit과 onRemove는 data state가 변화하면 재생성될 수 밖에 없는 함수들이기 때문이다
=> App component로 넘어가서 onEdit과 onRemove를 최적화 해준다
* App.js
// useCallback으로 묶은 후 dependency array에 빈배열을 넣는다
const onRemove = useCallback((targetId) => {
// 데이터를 다루는 부분을 잘라내기 해서
//const newDiaryList = data.filter((it) => it.id !== targetId);
// 데이터를 전달해서 return하는 방식으로 바꿔준다
setData((data) => data.filter((it) => it.id !== targetId));
// setData함수에 전달되는 파라미터data에 최신 state가 전달되는 것이기 때문에
// 항상 최신 state를 이용하기 위해서는 함수형update의 인자부분의 data를 사용해야 한다
// return부분의 data를 사용해야 최신형 update를 활용할 수 있다
}, []);
// onEdit함수도 마찬가지로 useCallback으로 묶어주고,
const onEdit = useCallback((targetId, newContent) => {
// 함수형으로 update를 시켜준다
setData((data) =>
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it
)
);
}, []);
=> 이렇게 해주면 하나의 item을 삭제할 때 나머지 item의 re-rendering이 일어나지 않고,
새로 추가를 해도 새로 추가된 아이템만 rendering이 일어난다
2024. 01. 11 (목)
프로그래머 도전기 102일차 (2) | 2024.01.14 |
---|---|
프로그래머 도전기 101일차 (0) | 2024.01.13 |
프로그래머 도전기 99일차 (2) | 2024.01.10 |
프로그래머 도전기 98일차 (2) | 2024.01.10 |
프로그래머 도전기 97일차 (2) | 2024.01.08 |