상세 컨텐츠

본문 제목

프로그래머 도전기 97일차

프로그래머가 될거야!

by Choyee 2024. 1. 8. 21:50

본문

오늘은

 

주말이었던 어제는 오랜만에 찜질방에 다녀왔습니다

정말정말 개운하게 목욕과 사우나를 하고 상쾌한 마음으로

오늘 새로운 한 주를 시작하였습니다

열심히 공부를 하고 무엇인가에 몰두하는 만큼

충분한 휴식도 중요한 것 같습니다

 

 

 

Spring 공부

 

<댓글 수정/삭제>

수정 -> 수정페이지 (해당 댓글 가져오기 - 상세보기 서비스) : GET
수정처리-> update 서비스 : POST

* detail.jsp

<P>
    <a href="/reply/update?boardId=${board.id}&id=${reply.id}">수정</a> | 
    <a href="/reply/delete?boardId=${board.id}&id=${reply.id}"
         onclick="return confirm('정말로 삭제하시겠습니까?')">삭제</a>
</P>

<c:choose>
    <c:when test="${empty reply.updateTime}">
        (작성일 : <fmt:formatDate value="${reply.createTime}"
        pattern="yyy-MM-dd HH:mm:ss" />)
    </c:when>
    <c:otherwise>
        (수정일 : <fmt:formatDate value="${reply.updateTime}"
        pattern="yyy-MM-dd HH:mm:ss" />)
    </c:otherwise>
</c:choose>

 



* ReplyController

public class ReplyController {
    // 삭제하기
    @GetMapping("/delete")
        public String replyDelete(@RequestParam("boardId") Long boardId,
                                 @RequestParam("id") Long id) {
        // 삭제할 땐 댓글 번호를 파라미터로 보내줌
        replyService.delete(id);
        return "redirect:/board?id=" + boardId;
    }

    // 수정 페이지 요청
    @GetMapping("/update")
        public String updateForm(@RequestParam("boardId") Long boardId,
                                @RequestParam("id") Long id,
                                Model model) {
        // 해당 댓글 가져오기
        ReplyDTO replyDTO = replyService.findById(id);
        model.addAttribute("reply", replyDTO);
        return "/board/replyupdate";
    }

    // 댓글 수정 처리
    @PostMapping("/update")
    public String update(@ModelAttribute ReplyDTO replyDTO) {
        log.info(""+replyDTO);
        replyService.update(replyDTO);
        return "redirect:/board?id=" + replyDTO.getBoardId();
    }
}




* ReplyService

public interface ReplyService {
	void delete(Long id);
	ReplyDTO findById(Long id);
	void update(ReplyDTO replyDTO);
}

 



* ReplyServiceImpl

public class ReplyServiceImpl implements ReplyService{

	@Override
	public void delete(Long id) {
		replyMapper.delete(id);
	}

	@Override
	public ReplyDTO findById(Long id) {
		return replyMapper.findById(id);
	}

	@Override
	public void update(ReplyDTO replyDTO) {
		replyMapper.update(replyDTO);
	}
}




* ReplyMapper 

public interface ReplyMapper {
    void delete(Long id);
    ReplyDTO findById(Long id);
    void update(ReplyDTO replyDTO);
}




* ReplyMapper.xml

<mapper namespace="cohttp://m.khit.web.mapper.ReplyMapper">
    <delete id="delete">
   		delete from reply where id = #{id}
    </delete>

    <select id="findById" resultType="cohttp://m.khit.web.dto.ReplyDTO">
    	select * from reply where id = #{id};
    </select>

    <update id="update">
        update reply
        set replycontent = #{replyContent}, updatetime = now()
        where id = #{id};
    </update>
</mapper>



 

 

 

 

 

<<todo project>>
- Spring(FrameWork) + Jsp(화면구현) + mybatis(DB SQL 처리)

project 이름 : todoweb
기능구현 상세
- 등록 기능
- 목록 보기(페이징, 검색)
- 상세 보기
- 수정/삭제

디자인 라이브러리:부트스트랩
컨택스트: cohttp://m.khit.todo.web
의존성 주입 - pom.xml
jdk: 11
Spring version:5.2.7
1. lombok
2. mysql driver
3. mybatis
4. spring-mybatis



* HomeController

@Controller
public class HomeController {

    @GetMapping("/")
    public String index() {
   		return "index";
    }
}




* TodoController

package com.khit.todoweb.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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 com.khit.todoweb.dto.TodoDTO;
import com.khit.todoweb.service.TodoService;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@AllArgsConstructor
@Slf4j
@RequestMapping("/todo")
@Controller
public class TodoController {
	
	private TodoService todoService;
	
	@GetMapping("/register")
	public String registerForm() {
		return "/todo/register";
	}
	
	@PostMapping("/register")
	public String register(@ModelAttribute TodoDTO todoDTO ) {
		log.info("todoDTO:" + todoDTO);
		todoService.insert(todoDTO);
		return "/todo/register";
	}
	
	@GetMapping("/list")
	public String todoList(Model model) {
		List<TodoDTO> todoDTOList = todoService.findAll();
		model.addAttribute("todoList", todoDTOList);
		return "/todo/list";
	}
}




* TodoService

package com.khit.todoweb.service;

import cohttp://m.khit.todoweb.dto.TodoDTO;

public interface TodoService {
    void insert(TodoDTO todoDTO);
    List<TodoDTO> findAll();
}




* TodoServiceImpl

package com.khit.todoweb.service;

import java.util.List;

import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;

import com.khit.todoweb.dto.TodoDTO;
import com.khit.todoweb.mapper.TodoMapper;
import com.khit.todoweb.vo.TodoVO;

import lombok.AllArgsConstructor;

@AllArgsConstructor
@Service
public class TodoServiceImpl implements TodoService{

	private TodoMapper todoMapper;
	private ModelMapper modelMapper;
	
	@Override
	public void insert(TodoDTO todoDTO) {
		// DTO를 VO로 변환해야 -> DB에 데이터를 저장
		// 1. 모듈(ModelMapper)을 사용안한 경우
		/*TodoVO todoVO = TodoVO.builder()
				.title(todoDTO.getTitle())
				.writer(todoDTO.getWriter())
				.build();*/

//		todoMapper.insert(todoVO);
		
		// 2. 모듈(ModelMapper)을 사용한 경우
		TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
		todoMapper.insert(todoVO);
	}

	@Override
	public List<TodoDTO> findAll() {
		return null;
	}

	@Override
	public List<TodoDTO> findAll() {
		// vo를 dto로 변환해야 -> DB에서 데이터를 꺼냄
		// vo리스트 데이터 가져오기
		List<TodoVO> voList = todoMapper.findAll();
		// vo 리스트를 dto로 저장하고 반환함(람다식으로 구현)
		List<TodoDTO> dtoList = voList.stream()
								.map(vo -> modelMapper.map(vo, TodoDTO.class))
								.collect(Collectors.toList());
		return dtoList;
	}
}







* TodoMapper

package cohttp://m.khit.todoweb.mapper;

import java.util.List;

import cohttp://m.khit.todoweb.vo.TodoVO;

public interface TodoMapper {
    public String getTime();   // 현재 시간 테스트
    public void insert(TodoVO todoVO); //등록하기
    List<TodoVO> findAll();
}






* TodoMapper.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="cohttp://m.khit.todoweb.mapper.TodoMapper">
    <select id="getTime" resultType="String">
    	select now()
    </select>

    <insert id="insert">
    	insert into tbl_todo(title, writer)
    	values(#{title}, #{writer})
    </insert>

    <select id="findAll" resultType="cohttp://m.khit.todoweb.vo.TodoVO">
    	select * from tbl_todo order by tno desc
    </select>
</mapper>




스프링부트
Mapper = VO 사용
- VO : TodoVO (DB연동, SQL)
- Test : Junit = 오류가 있는지 테스트
- ModelMapper jar 의존성 주입
   -> @Bean으로 등록해야함
   -> config클래스 생성 - @Configuration

 

 


* pom.xml

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.4.5</version>
</dependency>




* TodoConfig

package cohttp://m.khit.todoweb.config;

import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 설정파일로 만들어주는 어노테이션
@Configuration
public class TodoConfig {

    // ModelMapper를 빈으로 등록함
    @Bean
    public ModelMapper modelMapper() {
    	return new ModelMapper();
    }
}




* TodoServiceImpl

import org.modelmapper.ModelMapper;

@AllArgsConstructor
@Service
public class TodoServiceImpl implements TodoService{
    private ModelMapper modelMapper;

    @Override
    public void insert(TodoDTO todoDTO) {
        TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
        todoMapper.insert(todoVO);
    }
}





* TodoVO

package cohttp://m.khit.todoweb.vo;

import java.sql.Timestamp;

import cohttp://m.khit.todoweb.dto.TodoDTO;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Builder
@NoArgsConstructor  //기본 생성자
@AllArgsConstructor  //파라미터가 있는 생성자
@Data //Getter, Setter, ToString
public class TodoVO {
    //필드
    private Long tno;
    private String title;
    private String writer;
    private Timestamp createDate;

}




Service, Controller = DTO 사용
- DTO : TodoDTO (전달, 유효성검사)
- 변환기 : 서비스계층


* TodoDTO

package cohttp://m.khit.todoweb.dto;

import java.sql.Timestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Builder   // getter, setter 를 쉽게 해줌
@NoArgsConstructor    // 기본 생성자
@AllArgsConstructor   // 매개변수(파라미터)가 있는 생성자
@Data   // = Getter, Setter, ToString
public class TodoDTO {
    // 필드
    private Long tno;
    private String title;
    private String writer;
    private Timestamp createDate;
}

 

 

 

 

 

 

 

 

 

 

React 공부

 

<<React Developer Tools>>
= chrome의 확장 도구
   -> React Developer Tools 검색, 설치
=> f12 개발자 모드 -> >> -> Components 탭 선택

* Components 탭
= 개발중인 React의 component 계층 구조를 파악해서 보여준다
   data와 key값도 보여준다
   어떤 state, ref, useEffect를 가지고 있는지 까지도 보여준다
   어떤 props를 받았는지도 보여준다
=> View Settings
     ->  Highlight updates when components render -> checked
     = component가 re-rendering되는 것을 테두리로 시각화해서 보여준다

 

 

 

 

<<최적화>>

<연산 결과 재사용>
=> Memoization 기법 사용, 이해하기
= 현재 일기 데이터를 분석하는 함수 제작
   해당 함수가 일기 데이터의 길이가 변화하지 않을 때 값을 다시 계산하지 않도록 하기

* Memoization
= 프로그래밍 기법에 가까운 내용
= 이미 계산 해 본 연산 결과를 기억 해 두었다가
   동일한 계산을 시키면, 다시 연산하지 않고 기억 해 두었던 데이터를 반환 시키게 하는 방법
   = 시험을 볼 때 이미 풀어본 문제는 다시 풀어보지 않아도 답을 알고 있는 것과 유사
= 연산 과정 최적화
=> 컴퓨터 : 용량만큼 충분한 답을 기억할 수 있음 + 절대 까먹지 않고, 절대 헷갈리지 않음
     -> 나올 수 있는 모든 문제의 답을 외워버림

* useMemo() 함수
=> useMemo()함수로 Memoization하고싶은 함수의 코드를 감싸준다
     첫번째 인자로 callback함수를 받아서 callback함수가 return하는 값
     연산을 최적화 시켜주는 기능을 한다


* App.js

import { useMemo } from "react";
const App = () => {
  // 최적화 함수
  // => getDiaryAnalysis에 useMemo()함수를 활용하여
  // return을 갖는 함수를 Memoization할 수 있다
  const getDiaryAnalysis = useMemo(
    // useMemo()함수로 Memoization하고싶은 함수의 코드를 감싸준다
    // getDiaryAnalysis가 useMemo()함수를 호출한 결과값 처럼 바뀜
    // 첫번째 인자로 callback함수를 호출하는 형태가 된다
    () => {
      if (data.length === 0) {
        return { goodcount: 0, badCount: 0, goodRatio: 0 };
      }
      console.log("일기 분석 시작");
      // 감정 점수에 따른 일기 분류
      const goodCount = data.filter((it) => it.emotion >= 3).length;
      const badCount = data.length - goodCount;
      const goodRatio = (goodCount / data.length) * 100.0;
      return { goodCount, badCount, goodRatio };
    },
    // getDiaryAnalysis함수를 호출한다고 하더라도
    // 두번째 인자인 [data.length]가 변화하지 않는 이상
    // 똑같은 return을 계산하지 않고 반환한다
    [data.length]
    // dependency array에 어떤 값이 변화할 때만 이 연산을 다시 수행 하도록 명시하게 되면
    // 함수를 값처럼 사용을 해서 연산 최적화를 할 수 있다
  );

  // re-rendering이 될 때 다시한번 실행이 된다
  // useMemo()를 활용하여 함수를 최적화하게 되면 그 함수는 더이상 함수가 아니게 된다
  // useMemo()가 callback함수의 return 값을 return하기 때문에
  // getDiaryAnalysis는 useMemo로부터 값만을 return 받게 된다
  // 그렇기 때문에 함수가 아닌 값으로써 사용하게 된다
  const { goodCount, badCount, goodRatio } = getDiaryAnalysis; //getDiaryAnalysis() X

  return (
    <div className="App">
      {/* 받은 데이터 rendering */}
      <div>전체 일기 : {data.length}</div>
      <div>기분 좋은 일기 개수 : {goodCount}</div>
      <div>기분 나쁜 일기 개수 : {badCount}</div>
      <div>기분 좋은 일기 비율 : {goodRatio}</div>
    </div>
  );
};



 

 

 

 

 

2024. 01. 08 (월)

관련글 더보기