스프링 다시 복습한다고 해서 정리하고 듣기로 했다.


 

스프링에서 데이터베이스를 쓰는 방법

 

간단한 기본 개념

- 순수 jdbc : Java Database Connectivity

자바에서 사용할 수 있도록 만들어졌기 때문에 스프링에 완전 적합하지는 않다.

단점: 반복적이고 지루한 코드 작성이 필요하며, SQL 예외 처리, 리소스 관리(연결, , 결과 집합 닫기 등) 등의 복잡한 작업을 직접 처리
사용은 하지 않을 것임

 

- Spring JDBC

독자성을 줄이고 스프링에 맞게 수정

 

- SQL Mapper와 ORM

SQL Mapper

개발자가 sql을 직접 작성해서 연결해줘야한다.

DBMS에 종속적이고, 비슷한 쿼리를 작성하더라도 작성할때마다 매핑을 해줘야하는 번거로움이 있다.

 

ORM은 무엇인가

Object-Relational Mapping의 약자이며, 객체와 관계형 데이터베이스 

장점 : DBMS에 종속적이지 않다

단점 : JPA가 복잡하기 때문에 학습하기 어렵다(돈을 주고 배워야한다.)

 

JPA만으로는 개발의 한계가 있기 때문에 마이바티스도 같이 사용한다.

 

-H2 데이터베이스

자바로 작성된 관계형 데이터베이스이다. 테스트하기에 유용하기 때문에 사용한다.

필수는 아니다. 

 


MyBatis

데이터베이스 쿼리와 객체 즉 프로그래밍 언어 코드를 분리해서 생산성을 높이기 위해 사용한다.

유연성, 간결성, 성능, 다양한 DBMS 지원

자동으로 캐시 기능을 지원하기 때문에 빠르게 동작한다.

 

https://start.spring.io/

 

이렇게해서 설치하자

 

Lombok 자주사용하는 것

@Getter @Setter

@NoArgsConstructor : 기본 생성자를 만들어주는 친구

@AllArgsConstructor : 모든 필드값을 사용하는 생성자

@RequiredArgsConstructor : final이나 @NotNull인 필드값만 사용하는 생성자, final로 선언된 필드의 의존성이 자동 주입 ( 생성자 주입을 통해서 )

@Data : @Getter, @Setter, @RequiredArgsConstructor 외 더 있다.

 

dependencies에 

	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0'
	runtimeOnly 'com.mysql:mysql-connector-j'

 

추가

 

# 구성 설저에 사용되는 파일

# 키-쌍 구조

application.yml(구조 파악하기 유용) < application.properties ( 우선순위가 높다. )

application.properties에 

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/kdt?useUnicode=yes&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=1234

mybatis.type-aliases-package=com.spring.boot.mapper
mybatis.mapper-locations=mybatis-mapper/*.xml

 

추가하기 저기서 username과 password는 자신의 데이터베이스 사용자로 바꾸어서 사용

 

Art 엔터 누르면 import를 쉽게 할 수 있다.

 

lombok으로 게터 세터를 사용했는데 저렇게 안나온다면 어떻게 해야하는가

인테리제이 내에서 프로그램을 깔아줘야한다.


위의 내용은 수업을 들으면서 정리한거라 조금 왔다갔다 했다. 일단 그냥 흐름을 정리해서 작성하고, MyBatis로 만든 CRUD와 검색기능 실습을 정리하고, 11월 8일 기준 오늘 수업한 jpa 내용과 실습도 정리할 것이다.

 

 

포스코X코딩온 웹 풀스택 과정을 수강하며 확실히 듣기 전보다 코딩능력이 증가했지만, 웹개발 공부가 거의 처음인 나에게 교육 진도가 정말 빨랐다. 그래서 지금까지 진행하며 배운 node.js와 mysql 그리고 리액트까지 살짝 다룰 수만 있을 정도이고, 어떤 작업이나 원하는 기능을 구현하고 싶을 때, 바로바로 구현하는 것에 어려움이 있다. 그래서 고민해본 결과 현재 진도를 나가고 있는 스프링부트를 배우는 것은 보류하고 node.js와 mysql 그리고 리액트를 원하는대로 능숙하게 다룰 수 있도록 강의와 개인 프로젝트를 진행하며 개인 실력을 기르기로 결심했다. 지금까지 정리하지 못한 블로그도 하루 1블로깅을 하며, 다시 정리하고자 한다. 일단 지금은 이전에 진행하던 리액트 개인 프로젝트를 완성하며, React와 Node.js를 능숙하게 다룰 수 있도록 노력할 예정이다. 블로그도 새로 팔까 고민중이다. 

 

[앞으로 하루동안 해야하는 것들]

default : 3시간 이상 집중(공부시간)

아침 운동,

1일 1블로깅

1일 1커밋

2일 1 백준/프로그래머스 문제풀기(번갈아가면서)

리액트 개인 프로젝트 (마무리 후 >> 포스코X코딩온 kdt에서 진행한 두 번째 프로젝트 완성 후 배포하여 마지막 프로젝트를 할 때 사용할 수 있도록 개발하기 >> 포트폴리오 작성)


 

[11월 6일 해야하는 것]

개인 프로젝트 로그인/회원가입 백엔드 구현

Node.js 강의 했던 부분 처음부터 실습 위주로 복습

1일 1블로깅

1일 1커밋

(코테 준비는 내일부터

 


[리액트 개인 프로젝트 로그인/회원가입 기능 구현]

로그인과 회원가입 구현은 bcrypt 암호화를 사용하여 구현했다.

 

먼저 bycrypt를 설치해준다.

npm i bcrypt

 

 컨트롤러에 적용

// /Controller/Cstory.js

const bcrypt = require("bcrypt");

 

이렇게 위에서 불러주고, 회원가입할때 암호화하여 패스워드를 보내주고 로그인할때 비교하여 비교한 값이 true라면 로그인 성공, false라면 로그인 실패를 해주면된다. 

 

먼저 회원가입 기능을 구현해보자 패스워드 암호화 함수를 따로 만들어준다.

// /Controller/Cstory.js

const bcrypt = require("bcrypt");
//나머지 부분들은 생략한다.
 
 //Auth 기능
 
 //비밀번호 암호화
const bcryptP = (pw) => {
    return bcrypt.hashSync(pw, 10);
}

pw는 암호화하고자 하는 데이터이고, 10은 salt값이다. 여기서 salt란 입력한 값에 붙이는 특정 값으로 보안을 높여준다. 여기서 주의할 점은 bcrypt는 단방향 암호화이기 때문에 복호화가 불가능하다. 복호화가 가능한 암호를 만들고 싶다면, crypto의 createCipheriv를 사용하면 된다.

 

그 다음에 회원가입을 할 때, 암호화를 하여 데이터를 넣어준다.

// /Controller/Cstory.js

const bcrypt = require("bcrypt");
//나머지 부분들은 생략한다.
 
 //Auth 기능
 
 //비밀번호 암호화
const bcryptP = (pw) => {
    return bcrypt.hashSync(pw, 10);
}

// 회원가입
const signup = async(req, res) => {

    try {
        const {email, nickname, pw} = req.body;
        const hashPw = bcryptP(pw);//위에서 만들어준 암호화 사용
        const signUp = await models.user.create({
            email: email,
            nick_name: nickname,
            pw: hashPw,
        });
        if(signUp){
            res.json({result:true});
        }    
    } catch (error) {
        console.log(error);
    }

}

 

이후에 로그인을 할때 비교해줄 함수를 만들고 이 함수를 적용하여 로그인 기능을 구현하면된다.

 

간편하게 보기위해 이전에 만들었던 회원가입 부분은 생략하도록 하겠다.

// /Controller/Cstory.js

const bcrypt = require("bcrypt");
//나머지 부분들은 생략한다.
 
 //Auth 기능
 
 //비밀번호 암호화
const bcryptP = (pw) => {
    return bcrypt.hashSync(pw, 10);
}

//비밀번호 비교
const conpareBcryptP = async (pw, dbpw) => {
    return await bcrypt.compare(pw, dbpw);
}

여기서 비교한 값을 비동기로 처리한 이유를 설명하자면, 암호를 비교한 후 일치한다면 true를 리턴해주고, 일치하지 않는다면 false를 리턴해준다. 근데 이 값을 받기 위해선 비동기로 처리해야하기 때문에 async await을 사용해주었다.

 

로그인할려고 하는 사용자가 보낸 데이터(이메일과 패스워드)를 구조분해할당으로 받고, 시퀄라이즈의 쿼리문으로 해당 이메일을 가진 튜플을 가져온다. 튜플이 존재하지 않을 경우 "해당 유저가 존재하지 않습니다"라는 msg랑 false값을 보내준다.

// /Controller/Cstory.js

const bcrypt = require("bcrypt");
//나머지 부분들은 생략한다.
 
 //Auth 기능
 
 //비밀번호 암호화
const bcryptP = (pw) => {
    return bcrypt.hashSync(pw, 10);
}

//비밀번호 비교
const conpareBcryptP = async (pw, dbpw) => {
    return await bcrypt.compare(pw, dbpw);
}


//로그인 데이터 보냄
const login = async(req, res) => {

    try {
        const {email, pw} = req.body;
        console.log("로그인 데이터", email, pw);
        const result = await models.user.findOne({
            where: {email},
        });
        if(!result){
            res.json({
                result : false,
                msg : "해당 유저는 존재하지 않습니다.",
            });
        } 
        
        if (result) {
			//튜플이 존재할 때
        }
    } catch (error) {
        console.log(error);
    }

 

이제 튜플 즉, 유저가 존재할 때 비밀번호가 일치에 따라 일치한다면 true값을 일치하지 않는다면 false값을 보내준다. 

// /Controller/Cstory.js

const bcrypt = require("bcrypt");
//나머지 부분들은 생략한다.
 
 //Auth 기능
 
 //비밀번호 암호화
const bcryptP = (pw) => {
    return bcrypt.hashSync(pw, 10);
}

//비밀번호 비교
const conpareBcryptP = async (pw, dbpw) => {
    return await bcrypt.compare(pw, dbpw);
}

//로그인 데이터 보냄
const login = async(req, res) => {

    try {
        const {email, pw} = req.body;
        console.log("로그인 데이터", email, pw);
        const result = await models.user.findOne({
            where: {email},
        });
        if(!result){
            res.json({
                result : false,//프론트에서 로그인했을때 받은 값이 false라면 msg를 p태그로 띄워주기
                msg : "해당 유저는 존재하지 않습니다.",
            });
        } 
        
        if (result) {
            const compare = await conpareBcryptP(pw, result.pw);
            console.log("비교 후 리턴 값", compare);//비교한 후의 리턴값을 받을려면 비동기로 받아야한다.
            if(compare){//compare이 true일 때,
                res.json({
                    result : true,
                    msg: "로그인 성공!"
                });
            } else{//compare이 false일 때,
                res.json({
                    result : false,
                    msg : "비밀번호가 일치하지 않습니다."
                })
            }

        }
    } catch (error) {
        console.log(error);
    }

 

이를 모두 합친 코드는 다음과 같다.

// /Controller/Cstory.js

const bcrypt = require("bcrypt");
//나머지 부분들은 생략한다.
 
 //Auth 기능
 
 //비밀번호 암호화
const bcryptP = (pw) => {
    return bcrypt.hashSync(pw, 10);
}

//비밀번호 비교
const conpareBcryptP = async (pw, dbpw) => {
    return await bcrypt.compare(pw, dbpw);
}

// 회원가입
const signup = async(req, res) => {

    try {
        const {email, nickname, pw} = req.body;
        const hashPw = bcryptP(pw);//위에서 만들어준 암호화 사용
        const signUp = await models.user.create({
            email: email,
            nick_name: nickname,
            pw: hashPw,
        });
        if(signUp){
            res.json({result:true});
        }    
    } catch (error) {
        console.log(error);
    }

}

//로그인 데이터 보냄
const login = async(req, res) => {

    try {
        const {email, pw} = req.body;
        console.log("로그인 데이터", email, pw);
        const result = await models.user.findOne({
            where: {email},
        });
        if(!result){
            res.json({
                result : false,
                msg : "해당 유저는 존재하지 않습니다.",
            });
        } 
        
        if (result) {
            const compare = await conpareBcryptP(pw, result.pw);
            console.log("비교 후 리턴 값", compare);//비교한 후의 리턴값을 받을려면 비동기로 받아야한다.
            if(compare){
                res.json({
                    result : true,
                    msg: "로그인 성공!"
                });
            } else{
                res.json({
                    result : false,
                    msg : "비밀번호가 일치하지 않습니다."
                })
            }

        }
    } catch (error) {
        console.log(error);
    }

 


구현을 하면서 겪었던 에러와 알게된 점

 

- 에러 : data must be a string and salt must either be a salt string or a number of rounds

bcrypt가 받는 데이터는 반드시 string  값이거나 salt값으로 받아야 한다.

근데 나는 포스트맨으로 비밀번호를 int값으로 보냈기 때문에 에러가 발생했었다.

 

- try catch문을 사용하는 이유 :

try catch문을 사용하지 않은 상태에서 에러가 발생하면 서버가 꺼진다. 이를 방지하기 위해 try catch문으로 에러를 받는 것이다. 또한 에러가 발생했을때 어떤 작업을 할지도 정할 수 있기 때문에 매우 유용하다.


앞으로 구현해야하는 작업들

[백엔드 : Node.js]

[구현해야하는 기능]

  • 메인페이지 정보 보내기 11/6 (완)
  • 회원가입 : 데이터 베이스로 정보 보내기 11/6  (완)  
  • 로그인 : 비밀번호 암호화 하기 JWT로 구현 11/6 (완)
  • 이야기 상세 페이지에서 수정과 삭제가 가능
  • 마이페이지 들어가 내가 적은 이야기 확인가능 
  • 이야기 업로드 가능

 

  • 이야기 업로드 기능 11/7 - 8
  • 사진 업로드 서버로 올렸다가 다시 받으면서 주소 간소화 성공하기(코프런 참고) 
  • 에디터 가져와서 벨로그처럼 기능할 수 있도록 하기(코프런 참고)
  • 챗지피티 api 가져와 맞춤법 검사 해주기
  • 마이페이지 : 사용자 아이디가 있는 이야기와 세계 가지고 오기

[프론트엔드 : 리액트] : 백엔드 마무리 후 작업

  • 메인페이지
  • 회원가입 페이지
  • 로그인 모달
  • 사용자의 작업이 가능하도록 토큰 부여(로그인 했을때 백엔드에서 true값과 로그인 성공 메세지를 전달 받으면 토큰 받기 토큰이 있을경우 이용할 수 있는 기능 보여주기)
  •  
  • 마이페이지
  • 스토리 상세페이지
  • 스토리 업로드 페이지(챗지피티 적용(백엔드에서))

2차 프로젝트를 시작하면서(물론 그전부터) 블로깅을 거의 안하게된거 같다ㅠㅠ 이제 포트폴리오도 준비하고 기술면접, 코딩테스트, 자소서 등 준비할게 많기 때문에 차근차근 최근것부터 예전 것을 정리해보며, 준비하고자 한다. 프론트엔드 개발자로써 제일 중요하다고 할 수 있는 리액트를 하나하나 파헤쳐보자

 


SPA

리액트 라우터를 먼저 알기 전에 리액트에서 라우터를 이용함으로써 개발하려고 하는 SPA에 대해 알고가야한다.

SPA란 Single Page Application의 약어로 단일 웹페이지로 돌아하는 어플리케이션을 말한다. 브라우저에서 자바스크립트를 이용해 단일 췝페이지 상의 HTML 요소를 동적으로 생성 및 조작할 수 있다.

다만 검색 엔진 최적화에는 적합하지 않은 방법이다. 이는 React, Svelte, Vue.js와 같은 라이브러리로 개발할 수 있다.

 

라우팅

그럼 라우팅이란 무엇일까? 라우팅이란 간단하 말하자면 경로를 지정하는 행위를 말한다. 사용자가 요청한 url에 맞는 페이지를 보여주는 것이고, 리액트에서 제일 많이 사용되는 것이 React Router이다.

 

React Router

결론적으로 리액트 라우터란 개발자가 주소별로 다른 컴포넌트를 보여주기 위해 사용하는 라이브러리라고 할 수 있다.  설치 명령어는 다음과 같다

npm i react-router-dom@6 #버전 6 지정 설치

주요 컴포넌트는  

- BrouserRouter

- Routes

- Route

- Link

- createBrowserRouter

위와 같이 있다. 하나씩 알아보자

 

BrouserRouter

▷ HTML5를 지원하는 브라우저의 주소를 감지한다.

▷  Router의 역할

▷ 새로고침 없이 새로운 컴포넌트를 랜더링 해준다.

▷ url마다 컴포넌트가 바뀔 떄, 바뀌는 부분의 최상단에 위치해야한다.

import {BrowserRouter} from 'react-router-dom';

설치한 리액트 라우터 돔에서 가져와서 사용하면 된다.

 

- Routes, Route

▷ 경로가 일치하는 컴포넌트를 렌더링해주도록 경로를 매칭하는 역할을 해준다.

아래와 같이 경로를 매칭한다.

<Routes>
	<Route path="/" element={<RouteMain />}><Route>
    <Route path="/product/:id" element={<RouteProduct />}><Route>
    <Route path="*" element={<RouteNotFound />}><Route>
</Routes>

path는 경로이고 element는 연결할 컴포넌트이다.

하지만 위처럼 사용하는 방식은 옛날에 사용한 방식이며, 최근에는 createBrowserRouter를 사용한다.

 

- createBrowserRouter

import {createBrowserRouter} from 'react-router-dom';

를 불러와 배열 안의 객체를 중첩으로 넣어서 사용한다.

- Link

경로를 변경한다. 기존에 사용하던 a태그는 새로고침으로 렌더링을 수행했지만 <Link> 컴포넌트는 페이지 전환을 방지한다.

 

- Outlet

중첩 라우트 렌더링: Outlet은 중첩 라우트의 부모 라우트 컴포넌트 내에 서 사용되어, 해당 부모 라우트의 URL과 일치하는 자식 라우트 컴포넌트 를 렌더링해준다

context를 이용하여 자식 라우트에 데이터 전송이 가능하며 자식 라우트에서는 useOutletContext()를 사용한다.


URL 파라미터

- useParams()

경로에 : 를 사용해 설정되는 것을 가져올 수 있다.

URL 쿼리스트링

- useSearchParams()

원하는 쿼리스트링을 보낼 수 있다.

  - useNavigate()

 

 

 

+ Recent posts