- 이미지 업로드가 가능한 게시판을 구현하기 위한 로직을 정리한다.
- 자바 문법에 대한 이해도가 낮기 때문에 먼저 로직을 정리해보고, 그 다음 코드로 구현해 보기로 했다.
🐥 무엇을 사용하는가?
- spring boot
- JPA
- gradle
- DB 는 어떻게 해야할까?
- h2 사용
- 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용. 나중에 정확한 데이터베이스 기술이 정해지고 나면 바꿔 끼울 것임 (인터페이스 필요)
🥚 요구사항 정리하기
🐥 일반적인 웹 애플리케이션 계층 구조

- 컨트롤러 : 웹 MVC 의 컨트롤러 역할
- 서비스 : 핵심 비즈니스 로직 구현
- 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB 에 저장하고 관리
- 도메인 : 비즈니스 도메인 객체 (주로 데이터베이스에 저장하고 관리됨)
🐥 데이터
- 게시글의 제목 String title
- 게시글의 내용 String content
- 이미지 String imagePath
- 작성 일자 LocalDateTime createdAt
- 작성자 String author
🐥 기능 분할하기
- 게시글 쓰기 시작
- 텍스트 입력
- 이미지 (임시) 업로드
- 이미지를 임시 저장 폴더에 저장
- 임시 저장한 파일을 <img> 태그를 통해 보여줌 (frontend)
- 게시글 저장 ← 이때 백엔드와 프론트엔드가 통신함 (Post)
- 임시 저장 폴더에 저장된 이미지를 실제 저장 폴더로 이동
- 글을 DB 에 저장
- 게시글 조회 ← 이때 백엔드와 프론트엔드가 통신함 (Get)
🐥 service
- 게시글 저장 (업로드)
- 게시글 조회
- 게시글 삭제
- 게시글 수정
🐥 임시 저장을 하는 이유
만약 사용자가 글과 이미지를 입력하는 도중에 글을 저장하지 않고 화면을 나간다면 쓸모 없는 이미지 파일이 임시 저장 폴더에 남게된다. 해당 이미지 파일들을 사용자가 없는 시간에 스프링 스케줄러를 통해 지워준다면 쓸모 없는 이미지가 용량을 차지하지 않을 것이다.
🥚 Solution
1. 엔티티 생성
- Post 엔티티
- 게시글의 제목
- 게시글의 내용
- 이미지 경로
- 작성 일자
- 작성자
2. 리포지토리 (저장소) 생성
- PostRepository는 Post 엔티티를 데이터베이스에 저장하고 조회하기 위한 인터페이스이다.
- Post 객체를 저장하기 위한 저장소!!
3. 서비스 생성
- PostService는 비즈니스 로직을 처리하는 클래스이다.
- 게시글을 저장하고 조회하는 기능을 제공한다.
4. 컨트롤러 생성
- PostController는 HTTP 요청을 처리함
- 이미지 업로드, 게시글 작성, 게시글 조회를 위한 메소드
- 업로드된 이미지는 임시 폴더에 저장되고, 게시글이 저장될 때 최종 폴더로 이동한다.
🚨 서비스와 컨트롤러의 차이
- 게시글 저장, 조회 vs 이미지 업로드, 게시글 작성, 게시글 조회? 기능이 겹치는 것이 아닌지
- 서비스는 비즈니스 로직을 처리하는 데 사용된다. 주로 애플리케이션의 핵심 기능을 구현하며, 데이터베이스 작업, 계산, 외부 API 호출 등과 같은 작업을 포함한다.
- 비즈니스 로직, 트랜잭션 관리, 유효성 검사 및 예외 처리
- 컨트롤러는 사용자 요청을 처리하고, 사용자에게 응답을 반환하는 데 사용된다. 주로 웹 요청을 처리하며, HTTP 요청을 수신하고 적절한 서비스 계층의 메서드를 호출하여 작업을 수행한다.
- HTTP 요청 처리, 응답 생성, 데이터 바인딩 및 유효성 검사
5. HTML 템플릿 생성 (frontend)
- write.html : 게시글 작성 페이지
- 이미지를 업로드하고, 제목, 내용, 작성자를 입력 하는 페이지
<h1>Write a Post</h1>
<form method="post" enctype="multipart/form-data" th:action="@{/uploadImage}">
<label>Upload Image: <input type="file" name="image"/></label>
<button type="submit">Upload</button>
</form>
<form method="post" th:action="@{/savePost}">
<label>Title: <input type="text" name="title"/></label><br>
<label>Content: <textarea name="content"></textarea></label><br>
<label>Author: <input type="text" name="author"/></label><br>
<input type="hidden" name="imagePath" th:value="${imagePath}"/>
<button type="submit">Save Post</button>
</form>
<div th:if="${imagePath}">
<h2>Uploaded Image</h2>
<img th:src="@{'/' + ${imagePath}}" alt="Uploaded Image"/>
</div>
- post.html : 게시글 조회 페이지
- 제목, 내용, 작성자, 작성일자, 이미지를 표시 하는 페이지
<body>
<h1 th:text="${post.title}">Title</h1>
<p th:text="${post.content}">Content</p>
<p th:text="${post.author}">Author</p>
<p th:text="${post.createdAt}">Created At</p>
<div>
<img th:src="@{'/' + ${post.imagePath}}" alt="Post Image"/>
</div>
</body>
6. 애플리케이션 설정
- H2 데이터베이스를 메모리 모드로 설정하여 테스트
- H2 콘솔을 활성화하여 데이터베이스를 확인할 수 있음
- 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용. 나중에 정확한 데이터베이스 기술이 정해지고 나면 바꿔 끼울 것임 (인터페이스 필요)
7. 테스트 코드 작성
- 서로 그 순서와 관계없이 서로 의존관계 없이 설계가 되어야 한다.
- 하나의 테스트가 끝날 때마다 바로 바로 저장소에서 지워져야 한다.
- JUnit 프레임워크로 테스트를 실행
- 단축키 : 클래스에서 Command + shift + t
- new test 가 만들어짐
- 예외 처리가 더 중요 ⭐️⭐️⭐️⭐️⭐️

🚨 multipart? 이미지 저장은 어떻게 해야할까?
- 실제로 DB에 사진을 저장하는 것은 비효율, 병목현상 등을 고려해 하지 않는다고 한다. 실제 사진은 서버의 특정 위치에 저장하도록 하고 DB에는 사진에 대한 정보만을 저장한다.
- Multipart 는 사용자가 업로드하는 파일을 서버 측에서 받는 방식이다. multipart 를 사용하면 하나의 요청으로 여러 개의 파일과 폼 데이터를 함께 전송할 수 있다. 이미지, 텍스트 데이터 등의 여러 종류의 데이터를 전송 가능하다.
🚨 h2?
- 인 메모리 DB (일반적인 데이터베이스와 달리 프로그램 구동 시 메모리(RAM) 에 데이터 저장)
- 애플리케이션이 종료되면 모든 메모리가 삭제된다.(휘발성)
- 별도의 설치가 필요 없으며 2MB 정도의 적은 용량을 필요로 하는 프로그램. 매우 가볍고 빠르다.
🐥 이게 뭐임… + 강의에서 까먹지 말아야 할 부분 모음
- @ResponseBody 를 쓰는 이유
- Http 통신 프로토콜에서 바디부에 이 데이터를 내가 직접 넣어주겠다는 뜻임
- api 를 보통 이런 식으로 사용
- JPA 란?
- JPA(Java Persistence API)
- 자바 진영에서 ORM 기술 표준으로 사용되는 인터페이스의 모음
- 실제적으로 구현된것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크
- ORM 이란?
- (Object-Relational Mapping)
- 우리가 일반 적으로 알고 있는 애플리케이션 Class 와 RDB(Relational DataBase)의 테이블을 매핑(연결) 한다는 뜻이며, 기술적으로는 어플리케이션의 객체를 RDB 테이블에 자동으로 영속화 해주는 것이라고 보면 됨
- JPA 를 사용하는 이유
- JPA는 반복적인 CRUD SQL을 처리해준다. JPA는 매핑된 관계를 이용해서 SQL을 생성하고 실행하는데, 개발자는 어떤 SQL이 실행될지 생각만 하면 되고, 예측도 쉽게 할 수 있다. 추가적으로 JPA는 네이티브 SQL이란 기능을 제공해주는데 관계 매핑이 어렵거나 성능에 대한 이슈가 우려되는 경우 SQL을 직접 작성하여 사용할 수 있다.
- 추가적으로 알아둬야 할 것은, 스프링에서 흔히 사용하는 것으로 알고있는 JPA는, JPA를 이용하는 spring-data-jpa 프레임워크이지 JPA는 아니다.
- getter, setter 단축키
- ctrl + enter
- JavaBean 규약

- private 이라 외부에서 바로 못 꺼내는데 getter, setter 으로 접근한다.
- 프로퍼티 접근 방식이라고도 한다.
- HttpMessageConverter
- 기존에는 viewResolver 가 동작했는데 ResponseBody 사용 원리로는 HttpMessageConverter 가 동작한다.
- 객체일 때 JsonConverter 로
- spring 에서 이미 세팅되어 있음
- spring 기본 객체 처리 라이브러리 : Mapping**Jackson**2HttpMessageConverter
- 클라이언트의 HTTP Accept 해더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해서 HttpMessageConverter 가 선택된다.
- TDD
- 테스트 주도 개발
- 먼저 틀을 (테스트) 를 만들고 나서 구현 클래스를 만들어서 테스트함
- 서비스 클래스 네이밍을 할 때는 비즈니스에 가까운 용어를 써야 한다.
- service 는 비즈니스에 의존적으로 보통 설계하고 리포지토리 같은 경우에는 약간 더 service 보다는 단순히 기계적으로 개발스럽게 용어들을 선택한다.
- service 역할 : 비즈니스 처리
- repository 역할 : 단순히 데이터 넣고 빼고
- 역할에 맞게 네이밍을 함
🐥 단축키 정리
- static import
- ⌥ + ↵
- return 값 자동
- ⌥ + ⌘ + V
- 테스트 코드 생성
- ⌘ + ⇧ + T
- 해당 객체가 선언된 파일로 이동
- ⌘ + B
- 이전에 실행했던 파일 재실행
- ^ + R
- 생성자, 게터와 세터 자동 생성
- ⌘ + N
- 한 단어 선택
- ⌥ + ↑
'봄봄 Spring 이 왔어요' 카테고리의 다른 글
[Spring] 클라이언트로 정보 전달하기 (0) | 2024.08.04 |
---|---|
[인프런 Inflearn - 김영한] 스프링 입문 (프로젝트 환경설정) (1) | 2024.07.11 |
- 이미지 업로드가 가능한 게시판을 구현하기 위한 로직을 정리한다.
- 자바 문법에 대한 이해도가 낮기 때문에 먼저 로직을 정리해보고, 그 다음 코드로 구현해 보기로 했다.
🐥 무엇을 사용하는가?
- spring boot
- JPA
- gradle
- DB 는 어떻게 해야할까?
- h2 사용
- 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용. 나중에 정확한 데이터베이스 기술이 정해지고 나면 바꿔 끼울 것임 (인터페이스 필요)
🥚 요구사항 정리하기
🐥 일반적인 웹 애플리케이션 계층 구조

- 컨트롤러 : 웹 MVC 의 컨트롤러 역할
- 서비스 : 핵심 비즈니스 로직 구현
- 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB 에 저장하고 관리
- 도메인 : 비즈니스 도메인 객체 (주로 데이터베이스에 저장하고 관리됨)
🐥 데이터
- 게시글의 제목 String title
- 게시글의 내용 String content
- 이미지 String imagePath
- 작성 일자 LocalDateTime createdAt
- 작성자 String author
🐥 기능 분할하기
- 게시글 쓰기 시작
- 텍스트 입력
- 이미지 (임시) 업로드
- 이미지를 임시 저장 폴더에 저장
- 임시 저장한 파일을 <img> 태그를 통해 보여줌 (frontend)
- 게시글 저장 ← 이때 백엔드와 프론트엔드가 통신함 (Post)
- 임시 저장 폴더에 저장된 이미지를 실제 저장 폴더로 이동
- 글을 DB 에 저장
- 게시글 조회 ← 이때 백엔드와 프론트엔드가 통신함 (Get)
🐥 service
- 게시글 저장 (업로드)
- 게시글 조회
- 게시글 삭제
- 게시글 수정
🐥 임시 저장을 하는 이유
만약 사용자가 글과 이미지를 입력하는 도중에 글을 저장하지 않고 화면을 나간다면 쓸모 없는 이미지 파일이 임시 저장 폴더에 남게된다. 해당 이미지 파일들을 사용자가 없는 시간에 스프링 스케줄러를 통해 지워준다면 쓸모 없는 이미지가 용량을 차지하지 않을 것이다.
🥚 Solution
1. 엔티티 생성
- Post 엔티티
- 게시글의 제목
- 게시글의 내용
- 이미지 경로
- 작성 일자
- 작성자
2. 리포지토리 (저장소) 생성
- PostRepository는 Post 엔티티를 데이터베이스에 저장하고 조회하기 위한 인터페이스이다.
- Post 객체를 저장하기 위한 저장소!!
3. 서비스 생성
- PostService는 비즈니스 로직을 처리하는 클래스이다.
- 게시글을 저장하고 조회하는 기능을 제공한다.
4. 컨트롤러 생성
- PostController는 HTTP 요청을 처리함
- 이미지 업로드, 게시글 작성, 게시글 조회를 위한 메소드
- 업로드된 이미지는 임시 폴더에 저장되고, 게시글이 저장될 때 최종 폴더로 이동한다.
🚨 서비스와 컨트롤러의 차이
- 게시글 저장, 조회 vs 이미지 업로드, 게시글 작성, 게시글 조회? 기능이 겹치는 것이 아닌지
- 서비스는 비즈니스 로직을 처리하는 데 사용된다. 주로 애플리케이션의 핵심 기능을 구현하며, 데이터베이스 작업, 계산, 외부 API 호출 등과 같은 작업을 포함한다.
- 비즈니스 로직, 트랜잭션 관리, 유효성 검사 및 예외 처리
- 컨트롤러는 사용자 요청을 처리하고, 사용자에게 응답을 반환하는 데 사용된다. 주로 웹 요청을 처리하며, HTTP 요청을 수신하고 적절한 서비스 계층의 메서드를 호출하여 작업을 수행한다.
- HTTP 요청 처리, 응답 생성, 데이터 바인딩 및 유효성 검사
5. HTML 템플릿 생성 (frontend)
- write.html : 게시글 작성 페이지
- 이미지를 업로드하고, 제목, 내용, 작성자를 입력 하는 페이지
<h1>Write a Post</h1>
<form method="post" enctype="multipart/form-data" th:action="@{/uploadImage}">
<label>Upload Image: <input type="file" name="image"/></label>
<button type="submit">Upload</button>
</form>
<form method="post" th:action="@{/savePost}">
<label>Title: <input type="text" name="title"/></label><br>
<label>Content: <textarea name="content"></textarea></label><br>
<label>Author: <input type="text" name="author"/></label><br>
<input type="hidden" name="imagePath" th:value="${imagePath}"/>
<button type="submit">Save Post</button>
</form>
<div th:if="${imagePath}">
<h2>Uploaded Image</h2>
<img th:src="@{'/' + ${imagePath}}" alt="Uploaded Image"/>
</div>
- post.html : 게시글 조회 페이지
- 제목, 내용, 작성자, 작성일자, 이미지를 표시 하는 페이지
<body>
<h1 th:text="${post.title}">Title</h1>
<p th:text="${post.content}">Content</p>
<p th:text="${post.author}">Author</p>
<p th:text="${post.createdAt}">Created At</p>
<div>
<img th:src="@{'/' + ${post.imagePath}}" alt="Post Image"/>
</div>
</body>
6. 애플리케이션 설정
- H2 데이터베이스를 메모리 모드로 설정하여 테스트
- H2 콘솔을 활성화하여 데이터베이스를 확인할 수 있음
- 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소를 사용. 나중에 정확한 데이터베이스 기술이 정해지고 나면 바꿔 끼울 것임 (인터페이스 필요)
7. 테스트 코드 작성
- 서로 그 순서와 관계없이 서로 의존관계 없이 설계가 되어야 한다.
- 하나의 테스트가 끝날 때마다 바로 바로 저장소에서 지워져야 한다.
- JUnit 프레임워크로 테스트를 실행
- 단축키 : 클래스에서 Command + shift + t
- new test 가 만들어짐
- 예외 처리가 더 중요 ⭐️⭐️⭐️⭐️⭐️

🚨 multipart? 이미지 저장은 어떻게 해야할까?
- 실제로 DB에 사진을 저장하는 것은 비효율, 병목현상 등을 고려해 하지 않는다고 한다. 실제 사진은 서버의 특정 위치에 저장하도록 하고 DB에는 사진에 대한 정보만을 저장한다.
- Multipart 는 사용자가 업로드하는 파일을 서버 측에서 받는 방식이다. multipart 를 사용하면 하나의 요청으로 여러 개의 파일과 폼 데이터를 함께 전송할 수 있다. 이미지, 텍스트 데이터 등의 여러 종류의 데이터를 전송 가능하다.
🚨 h2?
- 인 메모리 DB (일반적인 데이터베이스와 달리 프로그램 구동 시 메모리(RAM) 에 데이터 저장)
- 애플리케이션이 종료되면 모든 메모리가 삭제된다.(휘발성)
- 별도의 설치가 필요 없으며 2MB 정도의 적은 용량을 필요로 하는 프로그램. 매우 가볍고 빠르다.
🐥 이게 뭐임… + 강의에서 까먹지 말아야 할 부분 모음
- @ResponseBody 를 쓰는 이유
- Http 통신 프로토콜에서 바디부에 이 데이터를 내가 직접 넣어주겠다는 뜻임
- api 를 보통 이런 식으로 사용
- JPA 란?
- JPA(Java Persistence API)
- 자바 진영에서 ORM 기술 표준으로 사용되는 인터페이스의 모음
- 실제적으로 구현된것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크
- ORM 이란?
- (Object-Relational Mapping)
- 우리가 일반 적으로 알고 있는 애플리케이션 Class 와 RDB(Relational DataBase)의 테이블을 매핑(연결) 한다는 뜻이며, 기술적으로는 어플리케이션의 객체를 RDB 테이블에 자동으로 영속화 해주는 것이라고 보면 됨
- JPA 를 사용하는 이유
- JPA는 반복적인 CRUD SQL을 처리해준다. JPA는 매핑된 관계를 이용해서 SQL을 생성하고 실행하는데, 개발자는 어떤 SQL이 실행될지 생각만 하면 되고, 예측도 쉽게 할 수 있다. 추가적으로 JPA는 네이티브 SQL이란 기능을 제공해주는데 관계 매핑이 어렵거나 성능에 대한 이슈가 우려되는 경우 SQL을 직접 작성하여 사용할 수 있다.
- 추가적으로 알아둬야 할 것은, 스프링에서 흔히 사용하는 것으로 알고있는 JPA는, JPA를 이용하는 spring-data-jpa 프레임워크이지 JPA는 아니다.
- getter, setter 단축키
- ctrl + enter
- JavaBean 규약

- private 이라 외부에서 바로 못 꺼내는데 getter, setter 으로 접근한다.
- 프로퍼티 접근 방식이라고도 한다.
- HttpMessageConverter
- 기존에는 viewResolver 가 동작했는데 ResponseBody 사용 원리로는 HttpMessageConverter 가 동작한다.
- 객체일 때 JsonConverter 로
- spring 에서 이미 세팅되어 있음
- spring 기본 객체 처리 라이브러리 : Mapping**Jackson**2HttpMessageConverter
- 클라이언트의 HTTP Accept 해더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해서 HttpMessageConverter 가 선택된다.
- TDD
- 테스트 주도 개발
- 먼저 틀을 (테스트) 를 만들고 나서 구현 클래스를 만들어서 테스트함
- 서비스 클래스 네이밍을 할 때는 비즈니스에 가까운 용어를 써야 한다.
- service 는 비즈니스에 의존적으로 보통 설계하고 리포지토리 같은 경우에는 약간 더 service 보다는 단순히 기계적으로 개발스럽게 용어들을 선택한다.
- service 역할 : 비즈니스 처리
- repository 역할 : 단순히 데이터 넣고 빼고
- 역할에 맞게 네이밍을 함
🐥 단축키 정리
- static import
- ⌥ + ↵
- return 값 자동
- ⌥ + ⌘ + V
- 테스트 코드 생성
- ⌘ + ⇧ + T
- 해당 객체가 선언된 파일로 이동
- ⌘ + B
- 이전에 실행했던 파일 재실행
- ^ + R
- 생성자, 게터와 세터 자동 생성
- ⌘ + N
- 한 단어 선택
- ⌥ + ↑
'봄봄 Spring 이 왔어요' 카테고리의 다른 글
[Spring] 클라이언트로 정보 전달하기 (0) | 2024.08.04 |
---|---|
[인프런 Inflearn - 김영한] 스프링 입문 (프로젝트 환경설정) (1) | 2024.07.11 |