Full Stack 교육 회고록

10/4 - Spring & React[미니실습!- 댓글달기, 클래스 컴포넌트, Lifecycle, 리액트 훅]

순두부 호랑이 2022. 10. 4. 18:01
728x90
SMALL

spring

자바 플랫폼을 위한 오픈 소스 어플리케이션 프레임워크

 

Spring Framework

- 동적인 웹 사이트 개발을 위해 여러가지 서비스 제공

- 한국에서는 공공기관의 웹 서비스 개발 시 사용을 권장하는 전자정부 표준 프레임워크의 기반 기술로 사용

 

Spring Boot

- 스프링 프레임워크를 사용하기 위한 설정의 많은 부분을 자동화하여 사용자가 편하게 스프링을 활용할 수 있는 환경 제공

 

Spring 특징

1. POJO(Plain Old Java Object)

: 객체 간의 관계 구성시 별도의 API나 라이브러리를 사용하지 않는 POJO 구성만으로 가능하도록 제작

: getter/setter 등의 단순한 메서드를 가진 Object

 

[POJO가 사용되는 이유?]

- Java EE 등 중량 프레임 워크들을 사용하게 되면서 해당 프레임워크에 종속된 "무거운"객체를 만들게 된 것에 반발해서 사용하게 된 용어

- 특정 기술과 환경에 종속된 자바 코드는 가독성이 떨어져 유지보수에 어려움이 있음

- 또한 특정 기술의 클래스를 상속받거나 직접 의존하는 경우에는 확장성이 매우 떨어진다는 단점이 있음

- 객체지향 설계의 장점을 잃어버리게 됨

 

2. 독립적

    : 특정한 라이브러리나 컨테이너의 기술에 종속적이지 않음

 

3. Spring MVC

   : Spring에서 제공하는 웹 모듈로 Model, View, Controller 세가지 구성요소를 사용해 사용자의 다양한 HTTP Request를 처리하고 단순한 텍스트 형식의 응답부터 REST 형석의 응답은 물론 View를 표시하는 html을 return 하는 응답까지 다양한 응답을 할 수 있는 프레임 워크

 

[기본구조]

[처리 흐름]

1. FrontController 역할을 하는 DispatcherServlet이 HTTP Request를 받음

2. DispatcherServlet은 적절한 Controller(Handler)를 선택하는 일을  HandlerMapping에게 요청

3. HandlerMapping은 적합한 Handler?(Contoller)선택

4. DispatcherServlet은 선택된 Handler(Contoller)의 비즈니스 로직 실행 작업을 HandlerAdapter에게 위임

5. HandlerAdapter가 Handler(Controller)의 비즈니스 로직을 호출하고 결과를 ModelAndView 객체에 담아서 DispatcherServlet에게 return

6. DispatcherServlet이 ViewResolver 를 이용하여 결과를 보여줄 View를 가져옴

7. View 객체에게 DispatcherServlet이 응답 결과 생성 요청

 

[구성요소]

- DispatcherServlet: 제일 앞단에서 HTTP Request를 처리하는 Controller

   : HttpRequest를 처리할 Controller 지정, Super Contreoller

   : 앞쪽에서 처리할 컨트롤러를 두는 패턴을 Front Controller 패턴이라고 함

- HandlerMapping: DispatcherServlet 으로 부터 검색을 요청받은 Controller(Handler)를 찾아 DispatcherServlet으로 정보 리턴

- HandlerAdapter: HandlerMapping을 통해 찾은 Controller(Handler)를 직접 실행

- Handler(Controller) : 어떤 Service로 처리할 것인지 결정하고 호출

- ModelAndView: 데이터와 뷰 저장

- ViewResolver: 결과를 출력할 View 객체 구한 후 내용 생성

 

4.DI(Dependency Injection): 의존성 주입

  - 의존관계(Dependency)란? 

     : 의존 대상 B가 변하면 그것이 A에 영향을 미칠 때 A는 B와 의존관계라고 함

ex)

-> 두 클래스의 결합성이 높음(유연성이 떨어짐)

-> 객체들 간의 관계가 아닌 클래스 간 관계가 맺어짐

 

- 의존 관계 주입(DI)란?

   : 의존관계를 외부에서 결정(주입)해주는 것

   : DI를 담당하는 컨테이너가 존재(IoC 컨테이너)

      [방법1: 생성자 활용]

     : 보통 주입받은 객체가 변하지 않거나 반드시 객체의 주입이 필요한 경우 사용

      [방법2: setter 메서드 활용]

        : 주입받는 객체가 변경될 가능성이 있는 경우 사용

- 의존 관계 주입 장점

  1. 결합도가 줄어듬 : 주입받는 대상이 바뀌더라도 해당 객체의 구현 자체를 수정할 일은 없음

  2. 유연성이 높아짐

  3. 테스트가 쉬어짐: 의존하고 있는 인터페이스가 어떻게 구현되어 있는지 몰라도 됨

  4. 가독성이 높아짐

 

5. AOP(Aspect-Oriented Programming)

   : 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화(하나의 단위로 묶는 것) 하겠다는 것

  ex) 데이터 베이스 연결, 파일 입출력 

        : 공통 로직을 제거할 수 있는 방법 제공

 

전자정부프레임워크(eGovFramework)

오픈 소스 기반으로 대한민국 정부가 만든 표준 프레임 워크

https://www.egovframe.go.kr/home/ntt/nttRead.do?pagerOffset=0&searchKey=&searchValue=&menuNo=65&bbsId=4&nttId=1743 

 

교육자료 | 표준프레임워크 포털 eGovFrame

처리중입니다. 잠시만 기다려주십시오.

www.egovframe.go.kr

 

1. Spring Project 구조

 

2. Annotation 정리

@Controller: 컨트롤러 지정, Model 객체(데이터 저장) 만들어 View 반환

@RequestMapping: 요청과 특정 메서드 매핑(value, method 사용)

@Autowired: 스프링 의존성 주입에 사용

@GetMapping: get 요청 매핑(5버전 이후부터 사용 가능)

@PostMapping : post 요청 매핑(5버전 이후부터 사용 가능)

@RequestParam: 요청 파라미터를 각 데이터를 따로 받을 수 있는 방식

@ModelAttribute : 요청 파라미터를 오브젝트로 피라미터를 받을 수 있는 방식

 

3.repository 정리

`<!-- mybatis -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.5</version>
</dependency>`

`<!-- mybatis-spring -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.7</version>
</dependency>`

`<!--  spring-jdbc -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.3.14</version>
</dependency>`

`<!-- MySQL 설정 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.49</version>
</dependency>`

  
`<!-- hikari cp 설정  : 커넥션 풀 관리용-->
<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>4.0.3</version>
</dependency>`

4. DB 연결

- root-context.xml

<!-- 커넥션 설정 정보 작성 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC"/>
		<property name="username" value="test"/>
		<property name="password" value="1234"/>	
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<constructor-arg ref="hikariConfig"/>
</bean>
<!-- 커넥션 설정 정보 작성 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC"/>
		<property name="username" value="test"/>
		<property name="password" value="1234"/>	
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<constructor-arg ref="hikariConfig"/>
</bean>

- Mapper interface 작성

public interface MemberMapper {

	public void joinMember(Member member);

	public Member loginMember(Member member);
	
	public void updateMember(Member member);
	
	public List<Member> selectAllMember();
	
	public void deleteMember(String id);

}

- Mapper XML 작성

  - 인터페이스 이름과 namespace 경로 일치 시킬 것

  - 메서드 이름과 각 태그 id 값 일치 시킬 것

<mapper namespace="com.smhrd.mapper.MemberMapper">

<insert id="joinMember" parameterType="com.smhrd.model.Member">
insert into member values (#{id}, #{pw}, #{nick})
</insert>
<select id="loginMember" parameterType="com.smhrd.model.Member" resultType="com.smhrd.model.Member">
	select * from member where id=#{id} and pw=#{pw}
</select>

<update id="updateMember" parameterType="com.smhrd.model.Member">
	update member set pw=#{pw},nick=#{nick} where id=#{id}
</update>

<select id="selectAllMember" resultType="com.smhrd.model.Member">
	select * from member
</select>

<delete id="deleteMember">
	delete from member where id=#{id}
</delete>

</mapper>

오후 리액트

<App.js>

import './App.css';
import Ex01 from './Example/Ex01'
import Ex02 from './Example/Ex02';
import Ex03 from './Example/Ex03'; 
import Ex04 from './Example/Ex04';
import Ex05 from './Example/Ex05';
import Ex06 from './Example/Ex06';


function App() {

    return (
      <div>

        {/* ex01 : useState 개념, React Event  개념 */}
        {/* <Ex01/> */}

        {/* ex02: useState 좋아요 예제  */}
        {/* <Ex02/> */}

        {/* ex03: useState를 활용한 랜덤숫자게임 */}
        {/* <Ex03/> */}

        {/* ex04: Map 함수를 활용한 발복 컴포넌트 생성 */}
        {/* <Ex04/> */}

        {/* ex05: Map 함수를 활용한 콜렉션 실습! */}
        {/* <Ex05/> */}

        {/* ex06: 미니실습! - 댓글달기 */}
        <Ex06/>
      </div>
  );
}

export default App;

<Ex06: 미니실습! - 댓글달기>

import React, { useState } from 'react'
import AddComment from './Ex06Com/AddComment'
import CommentList from './Ex06Com/CommentList'


//1. input 창에 댓글을 입력하고, +버튼을 누르면, 댓글 목록에 댓글 추가!

//2. 이때, 현재 시각과 같이 출력 ex) 여러분 안녕하세요 - 오후 4:57:37

// 'concat' 이라는 기능
// 배열A, 배열B
// let 배열 C = 배열A.concat(배열B)

//3. 엔터를 쳐도 똑같이 댓글이 등록되도록 할 것!

//** 이벤트 => onClick={함수이름}: 클릭했을 때!
//          => onChange={함수이름}: 변화가 있을 때!
//          => onKeyPress={함수이름} : 키보드 누를 때!
// input 창에 변화 감지 => 댓글 등록 버튼 input 값 저장 => map 함수로 목록에 뿌려줄 것

//** 실시간 시간 new Date().toLocaleTimeString()

const Ex06 = () => {

    const [com, setCom] = useState([{
    }])

    //하위 컴포넌트 -> 상위 컴포넌트로 값을 바로 전달 x
    // (1) 상위컴포넌트 내에 함수 생성
    // (2) 그 함수를 props 를 통해 하위 컴포넌트에 전달
    // (3) 하위 컴포넌트에서 함수 호출
    // (4) 값은? 매개변수 안에다가

    const handleComment = (newCom) => {
        console.log('handleComment', newCom)
        let curTime = new Date().toLocaleTimeString()
        //concat: 배열 +배열    
        setCom(com.concat({text : newCom, time:curTime}))
    }

return (
    <div>
        <h1>Leave your Comment!</h1>
        <AddComment handleComment={handleComment}/>
        <CommentList com={com}/>
    </div>
  )
}

export default Ex06

 

<AddComment.js>

import React, { useState } from 'react'

const AddComment = ({handleComment}) => {

    const [value, setValue] = useState('')

    // 1. input 내 변화 값 감지
    const inputHandle = (e)=>{
        setValue(e.target.value)
    }
    // 2. '+' 버튼을 클릭 시, input 값을 어딘가에 세팅
    const btnHandle = ()=>{
        console.log('현재 댓글', value)
        // 3. 세팅된 그 값을 상위 컴포넌트로 전송
        handleComment(value)
        // 4. input 창을 비워줄 것
        setValue('')
    }

    const enterHandle= (e)=>{
        // console.log(e.code)
        e.code === 'Enter' && btnHandle()
    }

  return (
    <div>
        <input onChange={inputHandle} value={value} onKeyPress={enterHandle}></input>
        <button onClick={btnHandle}>+</button>
    </div>
  )
}

export default AddComment

<CommentList.js>

import React from 'react'

const CommentList = ({com}) => {
    console.log('현재 댓글 상태',com)

    const smallTxt={
        color: 'gray',
        fontSize:'10px'
    }
  return (
    <div>
        {com.map((item, idx)=>(<p key={idx+item.text}>{item.text}
        <span style={smallTxt}>{item.time}</span></p>))}
    </div>
  )
}

export default CommentList

 

** 함수형 컴포넌트 구조 : rafce    /    클래스형 컴포넌트 구조 : rcc

 

[App.js]

import './App.css';
import Ex01 from './Example/Ex01'
import Ex02 from './Example/Ex02';
import Ex03 from './Example/Ex03'; 
import Ex04 from './Example/Ex04';
import Ex05 from './Example/Ex05';
import Ex06 from './Example/Ex06';
import Ex07 from './Example/Ex07';


function App() {

    return (
      <div>

        {/* ex01 : useState 개념, React Event  개념 */}
        {/* <Ex01/> */}

        {/* ex02: useState 좋아요 예제  */}
        {/* <Ex02/> */}

        {/* ex03: useState를 활용한 랜덤숫자게임 */}
        {/* <Ex03/> */}

        {/* ex04: Map 함수를 활용한 발복 컴포넌트 생성 */}
        {/* <Ex04/> */}

        {/* ex05: Map 함수를 활용한 콜렉션 실습! */}
        {/* <Ex05/> */}

        {/* ex06: 미니실습! - 댓글달기 */}
        {/* <Ex06/> */}

        {/* ex07 : 클래스 컴포넌트 */}
        <Ex07/>
      </div>
  );
}

export default App;

[Ex07.js: 클래스 컴포넌트]

// 함수형 컴포넌트 구조: rafce
// 클래스형 컴포넌트 구조 : rcc

//LifeCycle 어플리케이션의 생애주기
// a->b->c->d

import React, { Component } from 'react'

export default class Ex07 extends Component {
    //1. Class Component 에서 state 관리

    //a. constructor 최소 생성
    //    => state 관리, 변수선언, 데이터 관리
    constructor(props){
        super(props)
        console.log('a. constructor')
        this.state={
            num:0
        }
    }

    //2. 함수는 선언 없이 사용
    ck=()=>{
        console.log('ck')
        this.setState({
            num: this.state.num+1
        })
    }

    //b. render 화면을 렌더링 할 때 => 화면을 구성하는 요소
  render() {
    console.log('b. render')
    return (
      <div>
        <p>{this.state.num}</p>
        <button onClick={this.ck}>+1</button>
      </div>
    )
  }

  // c. componentDidMount 화면 마운트됨! => ApI Call
  componentDidMount(){
    console.log('c. componentDidMount')
  }

  // d. componentDidUpdate 값이 변경되어 갱신이 일어난 직후 호출
  componentDidUpdate(){
    console.log('update!')
  }
}

[Lifecycle]

React Component에는 생명 주기가 존재! 컴포넌트가 실행되거나, 업데이트 되거나, 삭제 될 때 특정 이벤트가 발생함

[ComponentDidUpdate]

갱신이 일어난 직후에 호출됨 최초 렌더링에서는 호출되지 않는다

사용 예시) state나 props 값이 갱신된 것을 확인할 때

 

[ComponentDidMount]

UI 세팅이 완료 되어 DOM에 접근이 가능하다

사용 예시)  API Call, setTimeout..etc

 

**Lifecycle은 Function Component에서 사용할 수 없다

 

[리액트 훅]

<React hooks>

함수형 컴포넌트가 클래스형 컴포넌트의 기능을 쓸 수 있도록 만들어주는 기능

<UseEffect(콜백함수 ()=>{}, 어레이[ ])>

React Component가 렌더링 될 때 마다 특정 작업을 할 수 있도록 해주는 리액트 훅

 

<정리>

** 하위 컴포넌트에서 상위 컴포넌트로 값을 전달하는 방법

- 상위 컴포넌트로 값을 바로 전달x

(1) 상위 컴포넌트 내에 함수를 생성

(2) 그 함수를 props를 통해 하위 컴포넌트로 전달

ex)

상위: <AddComment handleComment={handleComment}/>

하위: const AddComment = ({handleComment})=>{}

(3) 하위 컴포넌트에서 그 함수를 호출

(4) 만약 내가 보낼 값이 있다면? 매개변수를 통해

 

**class component

- 모든 문법을 전부 기억할 필요x

- class 컴포넌트의 존재를 인지 => 템플릿, 구글링 할 때 참고

- 단축키: rcc

 

**LifeCycle

- 생명 주기 => Function Component X React Hook UseEffect

 

- 컴포넌트가 첫 실행 (Mount)

   - constructor: 변수생성, state 생성, 데이터 관리

   - render: 화면 렌더링 중.. 화면 구성하는 요소들

   - componentDidMount: 화면 렌더링 끝! => API Call

 

- 컴포넌트가 업데이트(Update)

     - componentDidUpdate

- 컴포넌트가 화면에서 사라졌을 때 (Unmount)

728x90
LIST