상세 컨텐츠

본문 제목

프로그래머 도전기 100일차

프로그래머가 될거야!

by Choyee 2024. 1. 11. 21:54

본문

오늘은

 

오늘은 프로그래머 도전기 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 (목)

관련글 더보기