TIL

[์Šคํ”„๋ง] 2025-07-02

dev์Šค์นด์ด 2025. 7. 2. 21:27

๐Ÿ“ file.upload-dir=uploads/ ์˜๋ฏธ

์ด ์„ค์ •์€ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์œ„ํ•œ ์ €์žฅ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœํผํ‹ฐ์ด๋‹ค.

 

๐Ÿ› ๏ธ  ์‚ฌ์šฉ ์˜ˆ์‹œ

๐Ÿ“ application.properties

file.upload-dir=uploads/
  • file.upload-dir์€ Spring Boot์—์„œ ์ง์ ‘ ์ œ๊ณตํ•˜๋Š” ๊ณต์‹ ํ‚ค๋Š” ์•„๋‹ˆ๊ณ , ๊ฐœ๋ฐœ์ž๊ฐ€ ์ปค์Šคํ…€ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ํ‚ค์ด๋‹ค.
  • uploads/๋Š” ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋˜๋Š” ์ง€์ •๋œ ๊ฒฝ๋กœ ๊ธฐ์ค€์œผ๋กœ ํŒŒ์ผ์„ ์ €์žฅํ•  ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ์ด๋‹ค. (์ƒ๋Œ€ ๊ฒฝ๋กœ)

 

๐Ÿ“ Java ์ฝ”๋“œ (์˜ˆ: @Value ์‚ฌ์šฉ)

@Value("${file.upload-dir}")
private String uploadDir;
  • Java ์ฝ”๋“œ์—์„œ @Value๋กœ ์ฃผ์ž…๋ฐ›์•„ ํŒŒ์ผ ์ €์žฅ ๊ฒฝ๋กœ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

 

๐Ÿ“ ํŒŒ์ผ ์ €์žฅ ๋กœ์ง

Path filePath = Paths.get(uploadDir + filename);
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);

 

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด uploads/ ํด๋” ์•„๋ž˜์— ์—…๋กœ๋“œ๋œ ํŒŒ์ผ์ด ์ €์žฅ๋œ๋‹ค.

 

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ

  • uploads/ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์„œ๋ฒ„ ์‹คํ–‰ ์ „์— ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์„œ๋ฒ„ ์‹คํ–‰ ์‹œ ์ž๋™ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, ์ˆ˜๋™์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.
Files.createDirectories(Paths.get(uploadDir));

 

  • ์ƒ๋Œ€ ๊ฒฝ๋กœ(uploads/)๋Š” ํ˜„์žฌ ์‹คํ–‰๋˜๋Š” ์œ„์น˜ ๊ธฐ์ค€์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Spring Boot๋ฅผ build/libs ์•„๋ž˜ JAR๋กœ ์‹คํ–‰ํ•˜๋ฉด uploads/๋„ ๊ฑฐ๊ธฐ์— ๋งŒ๋“ค์–ด์ง„๋‹ค.

โœ… @Controller๋ž€?

Spring์—์„œ Controller๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด, ๋ธŒ๋ผ์šฐ์ € ์•ฑ์—์„œ ๋ณด๋‚ธ ์š”์ฒญ์„ ๊ฐ€์žฅ ๋จผ์ € ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

@Controller
public class MyController {
    @GetMapping("/hello")
    public String hello() {
        return "hello.html";
    }
}
  • @Controller๋Š” Spring MVC์˜ ์ปจํŠธ๋กค๋Ÿฌ์ž„์„ ์„ ์–ธํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.
  • ์ฃผ๋กœ ๋ทฐ(View)๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.(HTML, JSP ๋“ฑ)
  • return ๊ฐ’์€ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ๋ช… (์˜ˆ : hello.html)

 

โœ… @RestController๋ž€?

@RestController
public class ApiController {
    @GetMapping("/api/greet")
    public String greet() {
        return "Hello, API!";
    }
}
  • @RestController๋Š” @Controller + @ResponseBody์˜ ์กฐํ•ฉ์ด๋‹ค.
  • ์ฆ‰, ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ JSON/XML ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๋ฐฑ์—”๋“œ API ์„œ๋ฒ„์—์„œ๋Š” ๊ฑฐ์˜ ๋Œ€๋ถ€๋ถ„ @RestController๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

๐Ÿ” ์š”์ฒญ ๋งคํ•‘ ์–ด๋…ธํ…Œ์ด์…˜

์–ด๋…ธํ…Œ์ด์…˜ ์„ค๋ช…
@GetMapping GET  ์š”์ฒญ ์ฒ˜๋ฆฌ (์˜ˆ : ์กฐํšŒ)
@PostMapping POST ์š”์ฒญ ์ฒ˜๋ฆฌ (์˜ˆ : ๋“ฑ๋ก)
@PutMapping PUT ์š”์ฒญ ์ฒ˜๋ฆฌ (์˜ˆ : ์ˆ˜์ •)
@DeleteMapping DELETE ์š”์ฒญ ์ฒ˜๋ฆฌ (์˜ˆ : ์‚ญ์ œ)
@RequestMapping ์œ„ ๋ชจ๋“  ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๋ฐ˜ํ˜• (GET/POST ๋“ฑ ์ง€์ • ๊ฐ€๋Šฅ)

 

๐Ÿ“ฅ ํŒŒ๋ผ๋ฏธํ„ฐ ์ฒ˜๋ฆฌ

@GetMapping("/user")
public String getUser(@RequestParam String name) {
    return name + "๋‹˜ ์•ˆ๋…•ํ•˜์„ธ์š”!";
}
  • @RequestParam : ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง(?name = ์ด๋ฆ„)
  • @PathVariable : URL ๊ฒฝ๋กœ์˜ ์ผ๋ถ€ (/user/{id}
  • @RequestBody : JSON ์š”์ฒญ ๋ณธ๋ฌธ ํŒŒ์‹ฑ (์ฃผ๋กœ POST/PUT)

๐Ÿ“ค ์‘๋‹ต ๋ฐ˜ํ™˜ ๋ฐฉ์‹

๋ฐฉ์‹ ์„ค๋ช…
๋ฌธ์ž์—ด ๋ฐ˜ํ™˜ (String) ๋ทฐ ์ด๋ฆ„ (ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ) ํ˜น์€ ๋ฉ”์‹œ์ง€
ResponseEntity<T> HTTP ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์‘๋‹ต
@ResponseBody JSON ํ˜•์‹์œผ๋กœ ๋ฐ”๋กœ ๋ฐ˜ํ™˜

Controller - Service - Repository ๊ตฌ์กฐ

์ด ํŒจํ„ด์€ Spring์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ฒ˜(Layered Architecture)์ด๋‹ค. ์ด ๊ตฌ์กฐ๋Š” ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ(Separation of Concerns) ์›์น™์„ ์ž˜ ์ง€ํ‚ค๊ฒŒ ํ•ด์ค˜์„œ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์ง„๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋‚˜๋ˆ„๋Š” ์ด์œ ๋Š”?

  • Controller : ์›น ์š”์ฒญ ์ฒ˜๋ฆฌ์—๋งŒ ์ง‘์ค‘
  • Service : ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌ
  • Repository : ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์—๋งŒ ์ง‘์ค‘

โžก ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฌ์›Œ์ง€๊ณ , ๋ณ€๊ฒฝ์— ์œ ์—ฐํ•˜๋ฉฐ, ํ˜‘์—… ์‹œ ์—ญํ• ์ด ๋ช…ํ™•ํ•ด์ง„๋‹ค.

 

๐Ÿ“Œ ์ „์ฒด ๊ตฌ์กฐ ๊ทธ๋ฆผ

 

1๏ธโƒฃ Controller

์—ญํ• 

  • HTTP ์š”์ฒญ์„ ๋ฐ›๊ณ , ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ์™€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ณ„์ธต์ด๋‹ค.
  • ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ , ์„œ๋น„์Šค์— ์ „๋‹ฌํ•œ๋‹ค.

โ€ป ํŒŒ์‹ฑ (Parsing : ๊ตฌ๋ฌธ๋ถ„์„) ์€ ํ•˜๋‚˜์˜ ํ”„๋กœ๊ทธ๋žจ์„ ๋Ÿฐํƒ€์ž„ํ™˜๊ฒฝ (์˜ˆ๋ฅผ ๋“ค๋ฉด, ๋ธŒ๋ผ์šฐ์ € ๋‚ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„)์ด ์‹ค์ œ๋กœ ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ด๋ถ€ ํฌ๋งท์œผ๋กœ ๋ถ„์„ํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

 

์˜ˆ์‹œ

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public ResponseEntity<User> create(@RequestBody UserDto dto) {
        return ResponseEntity.ok(userService.createUser(dto));
    }
}

 

2๏ธโƒฃ Service

์—ญํ• 

  • ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ, ์—ฌ๋Ÿฌ ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์—ฐ์‚ฐ ๋“ฑ์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • Controller์™€ Repository์˜ ์ค‘๊ฐ„๋‹ค๋ฆฌ ์—ญํ• .

์˜ˆ์‹œ

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(UserDto dto) {
        User user = new User(dto.getName(), dto.getEmail());
        return userRepository.save(user);
    }
}

 

3๏ธโƒฃ Repository

์—ญํ• 

  • DB์— ์‹ค์ œ๋กœ ์ ‘๊ทผํ•œ๋‹ค. (Insert, Select, Update, Delete)
  • Spring Data JPA๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ JpaRepository๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค.

์˜ˆ์‹œ

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}