1. Base64란?
Base64는 바이너리 데이터(이미지, 파일 등)를 텍스트로 변환하는 인코딩 방식입니다. 64개의 ASCII 문자(A-Z, a-z, 0-9, +, /)와 패딩 문자(=)를 사용해 안전하게 전송할 수 있게 합니다.
2. 왜 사용하나요?
- 텍스트만 허용하는 시스템(예: JSON, 이메일, HTTP)에서 바이너리 데이터를 보낼 때.
- 데이터 손상 방지: 특수 문자가 바이너리에 섞이지 않음.
- 예: 플러터 앱에서 파일을 Base64로 변환해 서버로 업로드.
3. 어떻게 작동하나요?
- 바이너리 데이터를 8비트 바이트로 봅니다.
- 6비트 단위로 나눕니다 (3바이트 = 24비트 = 4개의 6비트).
- 각 6비트를 0-63 숫자로 변환 후, Base64 테이블에서 문자로 매핑.
- 나머지 비트가 부족하면 =로 패딩.
도식화 (간단 예: "Man" 문자열)
- 원본: M a n (ASCII: 77, 97, 110 → 바이너리: 01001101 01100001 01101110)
- 6비트 나누기: 010011 010110 000101 101110
- 10진수: 19, 22, 5, 46
- Base64 매핑: T W F u
- 결과: TWFu
import java.util.Base64;
// "Man"을 Base64로 인코딩
String original = "Man";
String encoded = Base64.getEncoder().encodeToString(original.getBytes());
// 결과: TWFu
Base64 역직렬화(디코딩)는 Base64 문자열을 원본 바이너리로 복원합니다.
- Base64 문자열을 4문자(24비트) 단위로 나눕니다.
- 각 문자를 6비트(0-63)로 매핑 후 결합해 8비트 바이트로 재구성.
- 패딩(=) 무시.
도식화 ("TWFu" 디코딩 예):
- Base64: T W F u (인덱스: 19, 22, 5, 46 → 6비트: 010011 010110 000101 101110)
- 8비트 재구성: 01001101 01100001 01101110 (ASCII: 77, 97, 110 → "Man")
import java.util.Base64;
byte[] decoded = Base64.getDecoder().decode("TWFu"); // "Man" 바이트 배열 반환.
# 서버 포트 설정
server:
port: 8080
# Spring 설정
spring:
# 애플리케이션 이름
application:
name: demo-img-server
# H2 데이터베이스 설정
datasource:
url: jdbc:h2:mem:testdb # 인메모리 데이터베이스
driver-class-name: org.h2.Driver
username: sa # 기본 사용자명
password: # 패스워드 없음
# H2 콘솔 활성화 (개발용)
h2:
console:
enabled: true # H2 웹 콘솔 사용
path: /h2-console # 콘솔 접근 경로
# JPA 설정
jpa:
hibernate:
ddl-auto: create-drop # 앱 시작시 테이블 생성, 종료시 삭제
show-sql: true # SQL 쿼리 로그 출력
properties:
hibernate:
format_sql: true # SQL 포맷팅
dialect: org.hibernate.dialect.H2Dialect
# 로깅 설정
logging:
level:
com.tenco.class_image_server: DEBUG # 우리 패키지는 DEBUG 레벨
org.springframework.web: DEBUG # 웹 관련 DEBUG
org.hibernate.SQL: DEBUG # Hibernate SQL DEBUG
사용자의 이미지 저장 요청할 때 사용
package com.tenco.class_image_server.dto;
import com.tenco.class_image_server.entity.Image;
import lombok.Data;
@Data
public class ImageRequestDto {
/**
* p_001.jpg, a.png
*/
private String fileName;
/**
* Base64로 인코딩된 이미지 데이터
* Flutter 에서 이미지를 Base64로 변환하여 전송할 예정
* 예: "data:image/jpeg;base64,/TwffajADFdadsff"
*
*/
private String imageData;
/**
* DTO 를 Entity로 변환하는 메서드 설계
*/
public Image toEntity() {
Image image = new Image();
image.setFileName(this.fileName);
image.setImageData(this.imageData);
return image;
}
}
ImageResponseDto - 리스트 만들 때 사용
package com.tenco.class_image_server.dto;
import com.tenco.class_image_server.entity.Image;
import lombok.Data;
@Data
public class ImageResponseDto {
// 이미지의 고유 ID
private Long id;
private String fileName;
// 1단계에서는 imageData 를 안 내려줄 예정
// 엔티티를 DTO로 변환하는 기능을 추가
public static ImageResponseDto fromEntity(Image image) {
ImageResponseDto dto = new ImageResponseDto();
dto.setId(image.getId());
dto.setFileName(image.getFileName());
return dto;
}
}
ImageDetailResponseDto - 상세보기 화면 만들 때 사용
package com.tenco.class_image_server.dto;
import com.tenco.class_image_server.entity.Image;
import lombok.Data;
@Data
public class ImageDetailResponseDto {
private Long id;
private String fileName;
private String imageData; // Base64 데이터 포함
// Image 엔티티에서 ---> ImageDetailResponseDto 객체를 생성하는 메서드 만들기
public static ImageDetailResponseDto fromEntity(Image image) {
ImageDetailResponseDto dto = new ImageDetailResponseDto();
dto.setId(image.getId());
dto.setFileName(image.getFileName());
dto.setImageData(image.getImageData());
return dto;
}
}
ImageService
package com.tenco.class_image_server.services;
import com.tenco.class_image_server.dto.ImageDetailResponseDto;
import com.tenco.class_image_server.dto.ImageRequestDto;
import com.tenco.class_image_server.dto.ImageResponseDto;
import com.tenco.class_image_server.entity.Image;
import com.tenco.class_image_server.repository.ImageRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ImageService {
private final ImageRepository imageRepository;
// 이미지 저장
public ImageResponseDto saveImage(ImageRequestDto requestDto) {
Image image = requestDto.toEntity();
// 데이터베이스 저장
Image savedImage = imageRepository.save(image);
// 엔티티에 들어가 데이터를 사용자에게 응답 --> DTO 로 변환해서 응답해야 함
return ImageResponseDto.fromEntity(savedImage);
}
// 모든 이미지 조회 서비스
public List<ImageResponseDto> getAllImage() {
List<Image> images = imageRepository.findAll();
List<ImageResponseDto> dtoList = new ArrayList<>();
for (Image image : images) {
dtoList.add(ImageResponseDto.fromEntity(image));
}
return dtoList;
}
// 1, 2, 3
// id 요청된 특정 이미지 상세 조회 서비스
public Optional<ImageDetailResponseDto> getImageDetailById(Long id) {
Optional<Image> optionalImage = imageRepository.findById(id);
if (optionalImage.isPresent()) {
return Optional.of(ImageDetailResponseDto.fromEntity(optionalImage.get()));
} else {
return Optional.empty();
}
}
public void deleteImage(Long id) {
imageRepository.deleteById(id);
}
}
ImageController
package com.tenco.class_image_server.controller;
import com.tenco.class_image_server.dto.ImageDetailResponseDto;
import com.tenco.class_image_server.dto.ImageRequestDto;
import com.tenco.class_image_server.dto.ImageResponseDto;
import com.tenco.class_image_server.services.ImageService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.w3c.dom.stylesheets.LinkStyle;
import javax.swing.text.html.Option;
import java.util.List;
import java.util.Optional;
@RestController // @Controller + @ResponseBody
@RequestMapping("/api/images")
@RequiredArgsConstructor
@CrossOrigin(origins = "*")
public class ImageController {
private final ImageService imageService;
// 이미지 업로드
@PostMapping
public ResponseEntity<ImageResponseDto> uploadImage(
@RequestBody ImageRequestDto requestDto) {
ImageResponseDto savedImageDto = imageService.saveImage(requestDto);
return ResponseEntity.ok(savedImageDto);
}
// 전체 이미지 조회
@GetMapping
public ResponseEntity<List<ImageResponseDto>> getAllImages() {
List<ImageResponseDto> images = imageService.getAllImage();
return ResponseEntity.ok(images);
}
// 특정 id로 이미지 상세 조회
// api/images/1
@GetMapping("/{id}")
public ResponseEntity<ImageDetailResponseDto> getImageById(@PathVariable Long id) {
Optional<ImageDetailResponseDto> image = imageService.getImageDetailById(id);
if(image.isPresent()) {
return ResponseEntity.ok(image.get());
} else {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteImage(@PathVariable Long id) {
imageService.deleteImage(id);
return ResponseEntity.ok().build();
}
}
Base64 이미지 인코딩
Base64 Image Encoder
show code copy image copy css ✘
www.base64-image.de

이미지 저장 테스트

이미지 저장 DB 확인

'Flutter' 카테고리의 다른 글
| 플러터 기본기 다지기 - "Everything is a Widget" Flutter의 핵심 철학 (1) | 2025.08.19 |
|---|---|
| 모두의 숙소 웹 만들기 (0) | 2025.08.19 |
| 쇼핑카트 앱 만들기 (0) | 2025.08.19 |
| 로그인 앱 만들기 (0) | 2025.08.19 |
| 프로필 앱 만들기 (1) | 2025.08.18 |