오늘 갑자기 외부에서 DDNS 주소로 Synology 접속이 되지 않았다.

 

로컬 네트워크로 접속을 해보니 Synology 에 부여된 IP 가 변경된 것으로 확인.. 

 

공유기에 설정해둔 포트포워딩을 바꿔주고, Synology IP 가 변경되지 않도록 정적 IP 로 변경 하였다.

 

변경 경로 : 제어판 - "네트워크" - "네트워크 인터페이스" - LAN 포트 1 편집 - [수동 구성 이용]

 

NAS 를 가족들이 접속 하다 보니.. 또 이런 일이 벌어지지 않도록 변경 하였다 ㅎㅎ

LGU+ 로 통신사 변경을 하면서 공유기를 변경하게 되었다.

 

변경 후 시놀로지 연동 했던 히스토리를 입력 한다.

 

인터넷 속도도 500MB로 변경하면서 기존 사용하던 자체 공유기가 지원을 못하는 것으로 보여.. 사용하지 않는 것으로 결정.

 

기존 연결 셋팅

인터넷 -> 통신사 공유기 -> 자체 공유기 -> NAS

 

변경 연결 셋팅

인터넷 -> 통신사 공유기 -> NAS

 

기존, 자체 공유기를 사용할 때 통신사 공유기를 "브릿지 모드"로 사용했지만, 자체 공유기를 제거하면서 기존 설정에서 "외부 (LTE, 5G)" 에서 접속이 가능하도록 설정이 필요했다..

 

구글링 해보니 유플러스 공유기 Admin 에 접속해서 Super DMZ 설정을 하라는 설명이 많았지만, 외부 (LTE) 에서 공인 IP 나 DDNS 로 접속이 안되는 것을 확인...

 

Super DMZ 가 아닌 그냥 "DMZ 사용" 으로 설정하고, 내부에서 사용하는 포트들을 포트포워딩에 등록해보니, 외부 (LTE) 환경에서 xxx.synology.me 로 접속되는 것을 확인 !

 

끝!

문제

주몽은 철기군을 양성하기 위한 프로젝트에 나섰다. 그래서 야철대장에게 철기군이 입을 갑옷을 만들라고 명령했다. 야철대장은 주몽의 명령에 따르기 위해 연구에 착수하던 중 갑옷을 만드는 재료들은 가각 고유한 번호가 있고, 갑옷은 2개의 재료로 만드는 데 2가지 재료의 고유한 번호를 합쳐 M (1 <= M <= 10,000,000)이 되면 갑옷이 만들어진다는 사실을 발견했다. 야철대장은 자신이 만들고 있는 재료로 갑옷을 몇 개나 만들 수 있는지 궁금해졌다. 야철대장의 궁금증을 풀어 주기 위해 N (1 <= N <= 15,000)개의 재료와 M이 주어졌을 때 몇 개의 갑옷을 만들 수 있는지를 구하는 프로그램을 작성하시오.

 

입력

1번째 줄에 재료의 개수 N (1 <= N <= 15,000), 2번째 줄에 갑옷을 만드는 데 필요한 수 M(1 <= M <= 10,000,000)이 주어진다. 3번째 줄에는 N개의 재료들이 가진 고유한 번호들이 공백을 사이에 두고 주어진다. 고유한 번호는 100,000보다 작거나 같은 자연수다.

 

출력

1번째 줄에 갑옷을 만들 수 있는 개수를 출력한다.

 

예제 입력 1

6	// 재료의 개수
9	// 갑옷이 완성되는 번호의 합
2 7 4 1 5 3	// 재료들

 

예제 출력 1

2

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class P1940_주몽의명령 {
	
	public static void main(String[] args) throws IOException {
    
    	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        int N = Integer.parseInt(br.readLine());        
        int M = Integer.parseInt(br.readLine());
        
        int[] A = new int[N];
        
        StringTokenizer st = new StringTokenizer(br.readLine());
        
        for(int i = 0; i < N; i++){
        	A[i] = Integer.parseInt(st.nextToken());
        }
        
        Arrays.sort(A);
        
        int st_index = 0;
        int ed_index = N - 1;
        int count = 0;
        
        while(st_index < ed_index){
        
        	if(A[st_index] + A[ed_index] < M){
            	st_index++;
            
            } else if(A[st_index] + A[ed_index] > M){
            	ed_index--;
            
            } else {
            	count++;
                st_index++;
                ed_index--;
            }
        
        }    
    }    
}

 

투 포인터 이동 원칙으로 접근하면 된다.

'일상이야기 > 알고리즘' 카테고리의 다른 글

(4) 연속된 자연수의 합 구하기  (0) 2022.04.18
(3) 구간 합 구하기  (0) 2022.04.13
(2) 평균 구하기  (0) 2022.04.13
(1) 숫자의 합  (0) 2022.04.13
알고리즘 스터디 시작  (0) 2022.04.12

어떠한 자연수 N은 몇 개의 연속된 자연수의 합으로 나타낼 수 있다. 당신은 어떤 자연수 N(1<=N<=10,000,000)을 몇 개의 연속된 자연수의 합으로 나타내는 가짓수를 알고 싶다. 이때 사용하는 자연수는 N 이여야 한다. 예를 들어 15를 나타내는 방법은 15, 7+8, 4+5+6, 1+2+3+4+5 이다. 반면, 10을 나타내는 방법은 10, 1+2+3+4이다. N을 입력받아 연속된 자연수의 합으로 나타내는 가짓수를 출력하는 프로그램을 작성하시오.

 

입력

1번째 줄에 정수 N이 주어진다.

 

출력

입력된 자연수 N을 연속된 자연수의 합으로 나타내는 가짓수를 출력한다.

 

예제 입력 1

15 // N

 

예제 출력 1

4

 

코드

import java.util.Scanner;

public class P2018_연속된자연수의합 {
	
	public static void main(String[] args) {
	
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		
		int count = 1;
		int start_index = 1;
		int end_index = 1;
		int sum = 1;
		
		while(end_index != N) {
			
			if(sum == N){
				count++;
				end_index++;
				sum = sum + end_index;
			
			} else if(sum > N){
				sum = sum - start_index;
				start_index++;
			
			} else {
				end_index++;
				sum = sum + end_index;
			}
			
		}
		System.out.println(count);
		
	}
}

 

투 포인터 이동 원칙을 이용해서 접근하면 되는 문제이다.

1~15 을 1번째부터 start_index, end_index 로 시작하고, sum 이 15가 될 때까지 end_index 를 이동시키고, 15가 되는 경우 start_index를 이동한다.

start_index -->
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
end_index -->

 

투 포인터 이동 원칙

sum > N : sum = sum - start_index; start_index++;

sum < N : end_index++; sum = sum + end_index;

sum == N : end_index++; sum = sum + end_index; count++;

 


Reference

백준의 올라인 저지 2018번

'일상이야기 > 알고리즘' 카테고리의 다른 글

(5) 주몽의 명령  (0) 2022.04.18
(3) 구간 합 구하기  (0) 2022.04.13
(2) 평균 구하기  (0) 2022.04.13
(1) 숫자의 합  (0) 2022.04.13
알고리즘 스터디 시작  (0) 2022.04.12

 

전 게시글은 아래를 참고 해주세요.

 

Spring Boot JPA 게시판 - CRUD (with MySQL)

Spring Boot 게시판 프로젝트를 시작하기 위해 Database 생성하고 끝냈었다. MySQL 접속 및 database 생성 MySQL을 Docker 에 설치하였고, Spring boot 와 연동하기 전 database 를 생성해보자. Docker에 MySQL을..

mkdevlab.tistory.com

 

게시글을 등록/수정 할 수 있는 기능을 추가해보자.

신규 생성한 소스

  • BoardRestController
  • BoardService
  • BoardRequestDto
  • BoardResponseDto

수정한 소스

  • Board (Entity)

 

1. Request(요청) Dto 생성

API로 요청할 때 데이터를 객체화 할 Request Dto 를 생성한다.

package com.mkdevlab.springbootboard.dto;

import com.mkdevlab.springbootboard.domain.Board;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BoardRequestDto {
	
	private String title;
	private String content;
	private String writer;
	private char deleteYn;
	
	public Board toEntity() {
		
		return Board.builder()
				.title(title)
				.content(content)
				.writer(writer)
				.deleteYn(deleteYn)
				.build();
	}

}

toEntity()

Entity, 즉 테이블에 해당 값을 넣기 위해 Entity 화를 해준다고 보면 된다.

 

2. 응답(Response) Dto 생성

요청이 오면 DB 에서 값을 조회하고 객체에 담아주기 위해 생성한다.

package com.mkdevlab.springbootboard.dto;

import java.time.LocalDateTime;

import com.mkdevlab.springbootboard.domain.Board;

import lombok.Getter;

@Getter
public class BoardResponseDto {

	private Long id;
	private String title;
	private String writer;
	private String content;
	private char deleteYn;
	private int hits;
	private LocalDateTime createdDate;
	private LocalDateTime modifiedDate;
	
	public BoardResponseDto(Board entity) {
		this.id = entity.getId();
		this.title = entity.getTitle();
		this.writer = entity.getWriter();
		this.content = entity.getContent();
		this.deleteYn = entity.getDeleteYn();
		this.hits = entity.getHits();
		this.createdDate = entity.getCreatedDate();
		this.modifiedDate = entity.getModifiedDate();
	}
}

 

 

3. Entity 수정 기능 추가

package com.mkdevlab.springbootboard.domain;

import java.time.LocalDateTime;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Board {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id; // PK
	
	private String title;
	
	private String content;
	
	private String writer;
	
	private int hits;
	
	private char deleteYn;
	
	private LocalDateTime createdDate = LocalDateTime.now();
	
	private LocalDateTime modifiedDate;

	@Builder
	public Board(String title, String content, String writer, int hits, char deleteYn) {
		this.title = title;
		this.content = content;
		this.writer = writer;
		this.hits = hits;
		this.deleteYn = deleteYn;
	}
	
	public void update(String title, String content, String writer) {
		this.title = title;
		this.content = content;
		this.writer = writer;
		this.modifiedDate = LocalDateTime.now();
	}
}

update() 메소드를 추가한다.

잉..? 근데 이건 그냥 Board의 값을 update 해주는 거지, 서버로 SQL 을 날리는 작업을 하는 건 보이지 않는다... WoW

 

4. Service 생성

package com.mkdevlab.springbootboard.service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mkdevlab.springbootboard.domain.Board;
import com.mkdevlab.springbootboard.dto.BoardRequestDto;
import com.mkdevlab.springbootboard.dto.BoardResponseDto;
import com.mkdevlab.springbootboard.repository.BoardRepository;


import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class BoardService {
	
	private final BoardRepository boardRepository;
	
	/*
	 * 게시글 생성 
	 */
	@Transactional
	public Long save(final BoardRequestDto param) {
		
		Board entity = boardRepository.save(param.toEntity());
		return entity.getId();
	}
	
	/*
	 * 게시글 리스트 조회
	 */
	public List<BoardResponseDto> findAll() {
		Sort sort = Sort.by(Direction.DESC, "id", "createdDate");
		List<Board> list = boardRepository.findAll(sort);
		
		// Stream API 를 사용하지 않는 경우		
	    List<BoardResponseDto> boardList = new ArrayList<>();
	    
	    for (Board entity : list) {
	        boardList.add(new BoardResponseDto(entity));
	    }
	    
	    return boardList;
		
		
		//return list.stream().map(BoardResponseDto::new).collect(Collectors.toList());
	}
	
	/*
	 * 게시글 수정
	 */
	@Transactional
	public Long update(final Long id, BoardRequestDto param) {
		
		//Board entity = boardRepository.findById(id).orElseThrow(() -> new NullPointerException());
				
		Board entity = boardRepository.findById(id).orElse(null);

	    if (entity == null) {
	        throw new NullPointerException();
	    }	   
	    
		entity.update(param.getTitle(), param.getContent(), param.getWriter());
		return id;
	}
}

save()

이 기능은 이 전 게시글에서 봤듯이 repository 의 save() 기능을 사용하고 있다.

 

findAll()

이것도 전 게시글에서 테스트 할 때 봣듯이 repository 의 findAll() 기능을 사용하고 있다.

 

update()

repository 의 update 기능이 없는건가? 라고 생각할 수 있는 부분인데, JPA 의 영속성 컨텍스트라는 개념이 등장한다.

가볍게 말하면 Spring 과 Database 사이에 Entity를 관리하는 영역이 있고, 그 영역에서 entity의 값이 바뀌면 물고 있다가 commit 이 일어나면 Database 로 변경된 값으로 저장하는 것이다.

 

@Transactional

service 클래스에서는 필수적으로 사용되고, 메소드 단위로 사용된다.

가볍게 말하면 이 녀석의 역할은 Transaction 을 시작(begin), 종료(commit), 예외(rollback) 처리해준다.

위에 update() 와 같이 Transaction 이 오류 없이 완료되면 commit 이 되기 때문에 Database 의 값이 변경된다고 생각하면 될 것 같다.

 

5. Controller 생성

package com.mkdevlab.springbootboard.controller;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.mkdevlab.springbootboard.dto.BoardRequestDto;
import com.mkdevlab.springbootboard.dto.BoardResponseDto;
import com.mkdevlab.springbootboard.service.BoardService;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class BoardRestController {
	
	private final BoardService service;
	
	@PostMapping("/boards")
	public Long save(@RequestBody BoardRequestDto param) {
		
		return service.save(param);
	}
	
	@GetMapping("/boards")
	public List<BoardResponseDto> findAll(){
		return service.findAll();
	}
	
	@PatchMapping("/boards/{id}")
	public Long save(@PathVariable Long id, @RequestBody BoardRequestDto param) throws Exception {
		return service.update(id, param);
	}

}

 

Advanced Rest client 를 사용해서 잘 작동하는 지 확인해보자.

 

1. 게시글 등록

7 이라는 숫자가 찍힌 걸 볼 수 있다. PK 로 지정했던 id 의 값이다. 이제 DB 에 저장되었는 지 확인해보자.

 

잘 저장 되었다!

 

 

2. 게시글 조회 (findAll())

리스트로 잘 나온다!

 

 

3. 게시글 수정

제목을 수정하고 SEND를 누르자 7 이라는 숫자로 Return 온 것을 확인하였다. 실제 DB를 조회해보자.

 

title 이 바뀐걸 볼 수 있고, modified_date 가 update 된 걸 볼 수 있다.

 

 

이것으로 게시글 CRUD 를 모두 살펴보았다.

 

이렇게 아주 간단하게 backend 영역을 살펴보았다.

 

다음에는 frontend 영역으로 이동해서 게시판 화면을 만들어보도록 하자!

 

끝.

 


Reference

https://congsong.tistory.com/55?category=749196 

 

'개발이야기 > Spring Boot' 카테고리의 다른 글

Spring Boot JPA 게시판 - CRUD (with MySQL)  (0) 2022.04.11
Spring Security  (0) 2022.04.10
ORM  (0) 2022.04.03

문제

수 N 개가 주어졌을 때 i 번째 수에서 j 번째 수까지의 합을 구하는 프로그램을 작성하시오

 

입력

1번째 줄에 수의 개수 N (1~100,000), 합을 구헤야 하는 횟수 M(1~100,000), 2번째 줄에 N개의 수가 주어진다. 각 수는 1,000 보다 작거나 같은 자연수다. 3번째 줄부터는 M개의 줄에 합을 구해야 한느 구간 i와 j가 주어진다.

 

출력

총 M개의 줄에 입력으로 주어진 i 번째 수에서 j 번째 수까지의 합을 출력한다.

 

예제 입력

5 3		//데이터의 개수, 질의 개수
5 4 3 2 1	//구간 합을 구할 대상 배열
1 3
2 4
5 5

예제 출력

12
9
1

 

먼저, 합의 배열을 만들자.

 

A : 보기 배열

S : 합 배열

 

S[ i ] = S[ i -1 ] + A [ i ]

 

구간 i ~ j 를 구하는 구간 합 공식으로 정답을 출력하자.

 

공식 : S[ j ] - S[ i - 1]

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class P11659_구간합구하기 {
	
	public static void main(String[] args) throws IOException {
		
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer stringTokenizer = new StringTokenizer(bufferedReader.readLine());
		
		int suNo = Integer.parseInt(stringTokenizer.nextToken());
		int quizNo = Integer.parseInt(stringTokenizer.nextToken());
		
		long[] S = new long[suNo+1];
		
		stringTokenizer = new StringTokenizer(bufferedReader.readLine());
		
		for(int i = 1; i <= suNo; i++){
			S[i] = S[i-1] + Integer.parseInt(stringTokenizer.nextToken());
		}
		
		for(int q = 0; q < quizNo; q++) {
			
			stringTokenizer = new StringTokenizer(bufferedReader.readLine());
			
			int i = Integer.parseInt(stringTokenizer.nextToken());
			int j = Integer.parseInt(stringTokenizer.nextToken());
			
			System.out.println(S[j]-S[i-1]);
			
		}
	}
}

 


Reference

백준 온라인 저지 11659번

 

'일상이야기 > 알고리즘' 카테고리의 다른 글

(5) 주몽의 명령  (0) 2022.04.18
(4) 연속된 자연수의 합 구하기  (0) 2022.04.18
(2) 평균 구하기  (0) 2022.04.13
(1) 숫자의 합  (0) 2022.04.13
알고리즘 스터디 시작  (0) 2022.04.12

문제

세준이는 기말고사를 망쳤다. 그래서 점수를 조작해 집에 가져가기로 결심했다. 일단 세준이는 자기 점수 중 최댓값을 골랐다. 그런 다음 최댓값을 M이라 할 때 모든 점수를 점수/M * 100 으로 고쳤다. 예를 들어 세준이의 최고점이 70점, 수학 점수가 50점이라면 수학 점수는 50/70 * 100이므로 71.43점이다. 세준이의 성적을 이 방법으로 계산했을 때 새로운 평균을 구하는 프로그램을 작성하시오.

 

입력

1번째 줄에 시험을 본 과목의 개수 N 이 주어진다. 해당 값은 1,000보다 작거나 같다. 2번째 줄에 세준이의 현재 성적이 주어진다. 해당 값은 100보다 작거나 같은, 음이 아닌 정수이고, 적어도 1개의 값은 0보다 크다.

 

출력

1번째 줄에 새로운 평균을 출력한다. 실제 정답과 출력값이 절대 오차 또는 상대 오차가 10^-2 이하이면 정답이다.

 

예제 입력

3
10 20 30

예제 출력

66.666666667

 

문제 분석

변환 점수의 평균을 구하는 식(점수가 A,B,C 인 경우)

(A/M * 100 + B/M * 100 + C/M * 100) / 3 = (A+B+C)*100/M/3

 

코드

import java.util.Scanner;

public class P1546_평균 {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		int A[] = new int[N];
		for(int i = 0; i < N; i++){
			A[i] = sc.nextInt();
		}
		
		long sum = 0;
		long max = 0;
		
		for(int i = 0; i < N; i++){
			if(A[i] > max){
				max = A[i];
			}
			sum += A[i];
		}
		
		System.out.println(sum*100.0/max/N);
		
	}
}

 

결과

 

Reference

백준 온라인 저지 1546번

'일상이야기 > 알고리즘' 카테고리의 다른 글

(5) 주몽의 명령  (0) 2022.04.18
(4) 연속된 자연수의 합 구하기  (0) 2022.04.18
(3) 구간 합 구하기  (0) 2022.04.13
(1) 숫자의 합  (0) 2022.04.13
알고리즘 스터디 시작  (0) 2022.04.12

문제

N 개의 숫자가 공백 없이 써 있다. 이 숫자를 모두 합해 출력하는 프로그램을 작성하시오.

 

입력

1번째 줄에 숫자의 개수 N(1 <= N <= 100), 2번째 줄에 숫자 N 개가 공백 없이 주어진다.

 

출력

입력으루 주어진 숫자 N 개의 합을 출력한다.

 

예제 입력 1

1 // 숫자의 개수
54321 // 공백 없이 주어진 N 개의 숫자

예제 출력 1

15

 

코드

import java.util.Scanner;

public class P11720_숫자의합 {
	
	public static void main(String[] args) {		
		
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		
		String sNum = sc.next();
		char[] cNum = sNum.toCharArray();
		int sum = 0;
		for (int i = 0; i < cNum.length; i++){
			sum += cNum[i] - '0';
		}
		
		System.out.println(sum);
	}
}

 

결과

Console 결과

 

Reference

백준 온라인 저지 11720번

 

 

'일상이야기 > 알고리즘' 카테고리의 다른 글

(5) 주몽의 명령  (0) 2022.04.18
(4) 연속된 자연수의 합 구하기  (0) 2022.04.18
(3) 구간 합 구하기  (0) 2022.04.13
(2) 평균 구하기  (0) 2022.04.13
알고리즘 스터디 시작  (0) 2022.04.12

자기개발로 알고리즘 공부를 시작해려고 한다.

 

큰 맘 먹고 알고리즘 코딩 테스트 책을 샀는데....(굳이 필요했을까? 라는 생각도 들지만.. 한 권 있으면 좋으니까 ㅎㅎ)

 

취준할 때는 백준 사이트를 애용했었는데, 요즘에는 다른 곳도 많이 사용하는 것 같다.

 

기업에서 코딩 테스트로 이용할 수 있는 사이트도 많이 생긴 것 같다.

 

이 책에 있는 내용을 한 바퀴 쭉 돌고, 여러 사이트를 돌면서 스스로 풀어봐야겠다.

 

그럼 시작~~

 

'일상이야기 > 알고리즘' 카테고리의 다른 글

(5) 주몽의 명령  (0) 2022.04.18
(4) 연속된 자연수의 합 구하기  (0) 2022.04.18
(3) 구간 합 구하기  (0) 2022.04.13
(2) 평균 구하기  (0) 2022.04.13
(1) 숫자의 합  (0) 2022.04.13

Spring Boot 게시판 프로젝트를 시작하기 위해 Database 생성하고 끝냈었다.

 

 

MySQL 접속 및 database 생성

MySQL을 Docker 에 설치하였고, Spring boot 와 연동하기 전 database 를 생성해보자. Docker에 MySQL을 설치하는 방법은 아래 글을 참고! (Mac) Docker 에 MySql 설치하기 1. Docker 공식 홈페이지의 가이드를 확..

mkdevlab.tistory.com

 

게시판에 사용 할 Table 을 만들고, Spring Data JPA 를 이용해서 Create, Read, Delete 를 구현해보자.

 

오늘은 JUnit 을 이용해서 Table 에 잘 들어가고, 삭제가 되는 지 Test 만 진행해보고자 한다.

 

그럼 시작 !

 

0. application.properties 수정

# datasource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mkdevlab?serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=password

# Resource and Thymeleaf Refresh
spring.devtools.livereload.enabled=true
spring.thymeleaf.cache=false

# JPA Properties
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.open-in-view=false
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

 

1. Spring Boot 프로젝트 생성

Type : Gradle

Java : 8

Packaging : Jar

 

Dependency 

  • Lombok
  • Thymeleaf
  • MySQL Driver
  • Spring Data JPA
  • Web
  • Spring Security (로그인도 같이 구현할 것이다)

 

2. 기초 Package 생성

  • config
  • controller
  • domain
  • dto
  • repository
  • service

 

3. domain - Board Entity 생성

 

package com.mkdevlab.springbootboard.domain;

import java.time.LocalDateTime;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Board {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id; // PK
	
	private String title;
	
	private String content;
	
	private String writer;
	
	private int hits;
	
	private char deleteYn;
	
	private LocalDateTime createdDate = LocalDateTime.now();
	
	private LocalDateTime modifiedDate;

	@Builder
	public Board(String title, String content, String writer, int hits, char deleteYn) {
		this.title = title;
		this.content = content;
		this.writer = writer;
		this.hits = hits;
		this.deleteYn = deleteYn;
	}
}

@Getter

getter 메소드를 생성해주는 lombok 의 기능입니다.

 

@NoArgsConstructor(access = AccessLevel.PROTECTED)

기본 생성자를 생성해주는 어노테이션으로, access 옵션을 추가하여 접근 범위를 설정할 수 있다.

동일 패키지 내에서만 해당 클래스를 접근 할 수 있도록 PROTECTED 로 설정.

 

@Entity

해당 클래스가 Table의 역할을 한다는 것을 명시하는 어노테이션이다.

별도로 테이블명을 옵션으로 지정할 수 있지만, board 라는 테이블을 생성할 것으로 패스.

 

@Id

PK 역할을 하는 컬럼을 지정 한다고 생각하면 된다.

 

@GeneratedValue(strategy = GenerationType.IDENTITY)

PK 생성 전략을 설정하는 어노테이션이라고 합니다.

IDENTITY 는 MySQL의 auto increment 기능을 구현해준다.

 

@Setter

setter 를 추가하지 않는 것은 Entity 객체는 Table 과 같으므로, 각 변수 = 컬럼의 정보에 대해 무작정 setter 가 된다고 하면 해당 컬럼의 데이터가 언제 어떻게 들어갔는 지 알 수 없다. Entity에서는 사용하지 않거나, 특정 컬럼 정보에 한해서 어노테이션을 적용해주면 되는 것 같다.

 

4. repository - BoardRepository 생성

package com.mkdevlab.springbootboard.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.mkdevlab.springbootboard.domain.Board;

public interface BoardRepository extends JpaRepository<Board, Long>{

}

Repository 는 interface 이고, JpaRepository 라는 인터페이스를 상속 받아 JPA 내 구현체를 사용할 수 있다.

JpaRepository 를 상속 받을 때 Generic 으로 Entity 와, PK 컬럼의 데이터 타입을 넣어주면 된다.

(MyBatis 와 정말 많이 다르다!!)

 

5. Create, Read, Delete Test

package com.mkdevlab.springbootboard.board;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.mkdevlab.springbootboard.domain.Board;
import com.mkdevlab.springbootboard.repository.BoardRepository;

@SpringBootTest
public class BoardTests {

	@Autowired
	BoardRepository boardRepository;
	
	@Test
	void save() {
		
		// 게시글 파라미터 생성
		Board param = Board.builder()
				.title("3번 게시글 제목")
				.content("3번 게시글입니다.")
				.writer("mkdevlab")
				.hits(0)
				.deleteYn('N')
				.build();
		
		// 게시글 저장
		boardRepository.save(param);
		
		Board entity = boardRepository.findById((long) 3).get();
		assertThat(entity.getTitle()).isEqualTo("1번 게시글 제목");
		assertThat(entity.getContent()).isEqualTo("1번 게시글입니다.");
		assertThat(entity.getWriter()).isEqualTo("mkdevlab");
		
	}
	
	@Test
	void findAll() {
		
		long boardsCnt = boardRepository.count();
		
		List<Board> boards = boardRepository.findAll();
		
	}
	
	@Test
	void delete() {
		
		//게시글 조회
		Board entity = boardRepository.findById((long)3).get();
		
		//게시글 삭제
		boardRepository.delete(entity);
		
	}
	
}

 

jUnit Test 는 호출 할 메소드를 더블클릭하고, 마우스 오른쪽 클릭 -> Run As -> jUnit Test 클릭으로 실행 가능하다.

 

 

save()

 

소스코드를 붙여넣기 전 2번 정보 테스를 했다. PK 값이 2로 상승을 했기 때문에 findById 할 때 3을 기입하였다.

 

앞서 생성한 BoardRepository 의 save() 메소드를 사용하여 데이터를 저장할 수 있다.

Lombok 의 Builder 로 값들을 셋팅해주고 save() 메소드를 호출 해본다.

jUnit 테스트 결과에 assertThat 으로 인해 Failure 가 발생하는 것으로 보기 위해 일부러 다른 값을 넣어보았다.

 

그 결과...

Table 에 값이 잘 들어갔다!!

 

Failure 로 떨어진게 보이고, 아래 뭐가 다른지 표시되었다.

 

findAll()

Count 를 조회한 것과 board 테이블을 조건 없이 전체 조회 한 것으로 볼 수 있다.

 

delete()

잘 삭제 됐다.

 

 

끝.

 

 


Reference:

https://congsong.tistory.com/51?category=749196 

 

'개발이야기 > Spring Boot' 카테고리의 다른 글

Spring Boot JPA 게시판 - 글 등록/수정 (with MySQL)  (0) 2022.04.13
Spring Security  (0) 2022.04.10
ORM  (0) 2022.04.03

+ Recent posts