๐Ÿ‘จโ€๐Ÿ’ป API ๊ฐœ๋ฐœ๋ถ€ํ„ฐ Postman ํ…Œ์ŠคํŠธ๊นŒ์ง€! (feat. ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฐฑ์—”๋“œ๋ฅผ ์ฒ˜์Œ ๋งก์•„๋ณด๋‹ˆ...)

hidihyeoneeยท2025๋…„ 3์›” 6์ผ
0
post-thumbnail

2025.03.06 ์ž‘์„ฑ

OS : Window
๊ฐœ๋ฐœํ™˜๊ฒฝ: IntelliJ IDEA
๊ฐœ๋ฐœ์–ธ์–ด: Java
ํ”„๋ ˆ์ž„์›Œํฌ: Spring Boot


๐Ÿ”ฅ 2์ฐจ ํ”„๋กœ์ ํŠธ ์‹œ์ž‘๊ณผ ์—ญํ• 

2์›” 24์ผ๋ถ€ํ„ฐ ์‹ ํ•œDS ๊ธˆ์œตSW ์•„์นด๋ฐ๋ฏธ 4๊ธฐ 2์ฐจ ํ”„๋กœ์ ํŠธ๊ฐ€ ์‹œ์ž‘๋๋‹ค.
์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ ํŒ€ ๋ฆฌ๋”๋ฅผ ๋งก์•„ ํŒ€์„ ์ด๋Œ๊ณ  ์žˆ๊ณ , ๋‚˜๋ฆ„ ์ž˜ ํ•ด๋‚˜๊ฐ€๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐ ์ค‘...๐Ÿ™‚

๊ธฐ์กด์—๋Š” ์•ฑ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์„ ์ฃผ๋กœ ๋‹ด๋‹นํ–ˆ์ง€๋งŒ, ์ด๋ฒˆ์—” ๋ฐฑ์—”๋“œ๊นŒ์ง€ ์ง์ ‘ ๊ฐœ๋ฐœํ•˜๊ฒŒ ๋˜๋ฉด์„œ ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๋Š” ๊ณผ์ •์„ ๊ฒช๊ณ  ์žˆ๋‹ค.

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋””์ž์ธ๊นŒ์ง€ ๋งก๊ฒŒ ๋˜์–ด UI/UX ๋””์ž์ธ ์—ญ๋Ÿ‰๋„ ํ•จ๊ป˜ ํ‚ค์šฐ๋Š” ์ค‘.
ํ•˜์ง€๋งŒ ๋„ˆ๋ฌด ํ”ผ๊ณคํ•ด์„œ ๋ฏธ์ณ๋ฒ„๋ฆฌ๊ฒ ์Œ!!

๋ฐฑ์—”๋“œ๋ฅผ ์ฒ˜์Œ ๋ฐฐ์šฐ๋ฉด์„œ ๋‹ค์‹œ ์ฝ”๋ฆฐ์ด๋กœ ๋Œ์•„๊ฐ„ ๊ธฐ๋ถ„์ด์ง€๋งŒ, ๋จผ ํ›—๋‚ ์—” ๋ฏ€์ฐ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด ์žˆ์„ ํ…Œ๋‹ˆ ๊ฑฑ์ • ์—†๋‹ค. ๐Ÿ˜Ž

๋‚ด๊ฐ€ ๋งก๊ฒŒ ๋œ ๋ฐฑ์—”๋“œ API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Œ

1) Domain : Account / Method : POST / URI: api/account/set-ratio / CRUD : UPDATE / Description : ์ฆ๊ถŒ๊ณ„์ขŒ์— ์ด์ฒด๋  ๋น„์œจ์„ ์„ค์ •ํ•˜๋Š” ๊ธฐ๋Šฅ / Response : x
2) Domain : Account / Method : POST / URI: api/account/collect-interest / CRUD : UPDATE / Description : ์ด์ž ๋ฐ›๊ธฐ ๊ธฐ๋Šฅ / Response : ์œ ์ €์˜ ์ž”์•ก์ •๋ณด๋ฅผ ๋ฆฌํ„ด
3) Domain : Account / Method : GET / URI: api/account/get-interest / CRUD : READ / Description : ์ด์ž์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค / Response : ์œ ์ €๊ฐ€ ๋ฐ›์„ ์ด์ž๋ฅผ ๋ฆฌํ„ด
4) Domain : Account / Method : GET / URI: api/account/histotry / CRUD : READ / Description : ์œ ์ € ๊ณ„์ขŒ์˜ ํžˆ์Šคํ† ๋ฆฌ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค / Response : ์œ ์ €์˜ ํŒŒํ‚นํ†ต์žฅ ํžˆ์Šคํ† ๋ฆฌ ์ •๋ณด

๊ฐœ๋ฐœ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

  1. DB ํ…Œ์ด๋ธ” ํ™•์ธ ๋ฐ JPA Entity ์ž‘์„ฑ
    • ๊ธฐ์กด AccountEntity์— ์ด์ฒด ๋น„์œจ (investor_ratio)์™€ ์ด์ž ์ •๋ณด(interest, interest_ratio) ์ถ”๊ฐ€
    • ๊ณ„์ขŒ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ €์žฅํ•  AccountHistoryEntity ์ถ”๊ฐ€
  2. DTO (Data Transfer Object) ์ž‘์„ฑ
    • ํด๋ผ์ด์–ธํŠธ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๊ธฐ ์œ„ํ•œ DTO ๋งŒ๋“ค๊ธฐ
  3. Repository (JPA ์ธํ„ฐํŽ˜์ด์Šค) ์ž‘์„ฑ
    • DB์™€ ์—ฐ๊ฒฐ๋˜๋Š” AccountRepository, AccountHistoryRepository ์ƒ์„ฑ
  4. Service (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง) ๊ตฌํ˜„
    • set-ratio, collect-interest, get-interest, history ๊ธฐ๋Šฅ ๊ตฌํ˜„
  5. Controller (API ์š”์ฒญ ์ฒ˜๋ฆฌ) ๊ตฌํ˜„
    • ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  AccountController ์ž‘์„ฑ
  6. ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ (๋”๋ฏธ ๋ฐ์ดํ„ฐ) ์‚ฝ์ž…
    • DB์— ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
  7. JPA ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ
    • ๊ธฐ๋Šฅ์ด ์ •์ƒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธ
  8. Postman์œผ๋กœ API ํ…Œ์ŠคํŠธ
    • ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ, ๊ธฐํš์ž๋‚˜ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์™€ ์˜์‚ฌ์†Œํ†ต์„ ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด Postman์œผ๋กœ API ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ณ  ๋ฌธ์„œํ™”ํ•ด์„œ ์ „๋‹ฌํ•ด์ฃผ๋ฉด ํ˜‘์—…ํ•˜๊ธฐ ์ข‹์Œ
    • ๋ฐฉ์‹์€ ํŒ€ ๋‚ด์—์„œ ์ž์œ ๋กญ๊ฒŒ ์ •ํ•ด์„œ ํ•˜๋ฉด ๋จ
    • ํŒ€ ๋‚ด์—์„œ ๊ฐ์ž ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ตฌ๊ธ€ ์‹œํŠธ์— ์ •๋ฆฌํ•˜๊ธฐ๋กœ ๊ฒฐ์ •
    • ์ด์ „ '๋ชจ์ž‰'์ด๋ผ๋Š” ํŒ€์—์„œ ์•ฑ ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž๋กœ ํ™œ๋™ํ–ˆ์„ ๋•Œ, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž ์นœ๊ตฌ๋“ค์ด ์ „๋‹ฌํ•ด์ค€ ๋ฌธ์„œ๊ฐ€ ์•„์ฃผ ์ข‹์•˜๋˜ ๊ธฐ์–ต์ด ์žˆ์–ด ๊ณต์œ ํ•จ.
      http://13.209.196.123/docs/api.html#Overview-Response

1. DB ํ…Œ์ด๋ธ” ํ™•์ธ ๋ฐ JPA Entity ์ž‘์„ฑ

AccountEntity

package org.team4.sol_server.domain.account.entity;

import jakarta.persistence.*;
import lombok.*;

import java.math.BigInteger;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "account")
public class AccountEntity extends BaseEntity {

//        @ManyToOne
//        @JoinColumn(name = "user_idx", nullable = false)
//        private UserEntity user;

        @Id
        @Column(name = "account_no", nullable = false, unique = true)
        private String accountNumber;

        // ์ž„์‹œ
        @Column(name = "user_idx", nullable = false)
        private int userIdx;

        @Column(name = "balance", nullable = false)
        private Long balance;

        @Column(name = "investor_ratio", nullable = false)
        private int investorRatio;  // ์ด์ฒด ๋น„์œจ

        @Column(name = "interest", nullable = false)
        private int interest;  // ์ด์ž ๊ธˆ์•ก

        @Column(name = "interest_ratio", nullable = false)
        private double interestRatio;  // ์ด์ž์œจ
}
  • ์ด์ฒด ๋น„์œจ (investor_ratio)์™€ ์ด์ž ์ •๋ณด(interest, interest_ratio) ์ถ”๊ฐ€

AccountHistoryEntity

package org.team4.sol_server.domain.account.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "account_history")
public class AccountHistoryEntity extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "account_his_idx")
    private int id;

    @ManyToOne
    @JoinColumn(name = "account_no", nullable = false)
    private AccountEntity account;

//    @ManyToOne
//    @JoinColumn(name = "user_idx", nullable = false)
//    private UserEntity user;

    // ์ž„์‹œ
    @Column(name = "user_idx", nullable = false)
    private int userIdx;

    @Column(name = "display_name", nullable = false)
    private String displayName;

    @Column(name = "pre_balance", nullable = false)
    private int preBalance;

}
  • ๊ณ„์ขŒ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ €์žฅํ•  AccountHistoryEntity

2. DTO (Data Transfer Object) ์ž‘์„ฑ

package org.team4.sol_server.domain.account.dto;

import lombok.*;

import java.math.BigInteger;

@Data
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AccountDTO {

    private String accountNumber;
    private Long balance;
}

๊ทผ๋ฐ ๊ฐœ๋ฐœ ์™„๋ฃŒํ•˜๊ณ  ์˜๋ฌธ์ ์ด ์ƒ๊น€...

DTO๋Š” ๊ผญ ํ•„์š”ํ•œ๊ฐ€?

๊ทธ๋ž˜์„œ ๊ฐ•์‚ฌ๋‹˜๊ป˜ ํ˜ธ๋‹ค๋‹ฅ ๋‹ฌ๋ ค๊ฐ€์„œ ์—ฌ์ญค๋ดค๋‹ค!

๊ฐ•์‚ฌ๋‹˜์™ˆ : ์‚ฌ์‹ค DTO? ์•ˆ ๋งŒ๋“ค์–ด๋„ ๋˜๊ธด ํ•ฉ๋‹ˆ๋‹ค...

์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ setTransferRatio()์™€ collectInterest() ๋‘ ๊ฐœ์˜ API๋ฅผ ๋น„๊ตํ•ด๋ณด์ž.

// ์ฆ๊ถŒ ๊ณ„์ขŒ ์ด์ฒด ๋น„์œจ ์„ค์ •
    @PostMapping("/set-ratio")
    // accountNumber --> ๊ณ„์ขŒ ๋ฒˆํ˜ธ ๋ฐ›์Œ, ratio --> ์ด์ฒด ๋น„์œจ(%) ๋ฐ›์Œ
    public ResponseEntity<String> setTransferRatio(@RequestBody TransferRatioDTO requestDTO) {
        accountService.setTransferRatio(requestDTO.getAccountNumber(), requestDTO.getRatio()); // ํˆฌ์ž ๋น„์œจ ์—…๋ฐ์ดํŠธ
        return ResponseEntity.ok("์ฆ๊ถŒ ๊ณ„์ขŒ ์ด์ฒด ๋น„์œจ ์„ค์ •: " + requestDTO.getRatio());
    }

// ์ด์ž ๋ฐ›๊ธฐ
    @PostMapping("/collect-interest")
    public ResponseEntity<AccountDTO> collectInterest(@RequestParam String accountNumber) {
        AccountDTO updatedAccount = accountService.collectInterest(accountNumber); // ํˆฌ์ž ๋น„์œจ์„ ๊ธฐ์ค€์œผ๋กœ ์ด์ž ๊ณ„์‚ฐ ๋ฐ ๊ณ„์ขŒ ์ž”์•ก ์ฆ๊ฐ€ ํ•จ์ˆ˜
        return ResponseEntity.ok(updatedAccount);
    }

์ด ๋‘˜์˜ ์ฐจ์ด์ ์ด ์žˆ๋‹ค.

setTransferRatio()์—์„œ๋Š” DTO(TransferRatioDTO)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ธด๋‹ค.

collectInterest()์—์„œ๋Š” @RequestParam์„ ์ง์ ‘ ์‚ฌ์šฉํ•œ๋‹ค.

๊ทผ๋ฐ ๋‚˜ ๊ฐ™์€ ๊ฒฝ์šฐ์— ์‹ค์ œ๋กœ TransferRatioDTO๋ฅผ ๋ณด๋ฉด

package org.team4.sol_server.domain.account.dto;

import lombok.*;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TransferRatioDTO {
    private String accountNumber;
    private int ratio;
}

์ด๋ ‡๊ฒŒ ๋ณ€์ˆ˜๊ฐ€ 2~3๊ฐœ ์ •๋„๋ฐ–์— ์—†๋Š”๋ฐ...
๊ตณ์ด DTO๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒŒ ํ•„์š”ํ• ๊นŒ?๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์–ด ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๊ฐ•์‚ฌ๋‹˜ํ•œํ…Œ ์—ฌ์ญค๋ณธ ๊ฒฐ๊ณผ...

๊ฒฐ๋ก 

  • PostMapping์˜ ๊ฒฝ์šฐ @RequestParam๊ณผ @RequestBody(DTO) ๋ชจ๋‘ ๊ฐ€๋Šฅ.
  • ํŒ€ ๋‚ด ์ฝ”๋“œ ์ปจ๋ฒค์…˜์„ ์ •ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋จ.
    ์˜ˆ๋ฅผ ๋“ค์–ด, 5๊ฐœ ์ด์ƒ์ด๋ฉด DTO๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜์ž! ๋“ฑ.
  • DTO์˜ ์žฅ์ : ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์ข‹๊ณ , ํ˜‘์—…ํ•  ๋•Œ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง.
  • DTO๊ฐ€ ๋ถˆํ•„์š”ํ•œ ๊ฒฝ์šฐ: ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹จ์ˆœํ•œ ๊ฒฝ์šฐ (2~3๊ฐœ ์ดํ•˜).

์•„๋ฌดํŠผ DTO๋Š” ๊ผญ ์กด์žฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์‚ฌ์‹ค์ด๋‹ค.

๋ฐ์ดํ„ฐ๊ฐ€ ๋‹จ์ˆœํ•œ ๊ฒฝ์šฐ ์•ˆ ์จ๋„ ๋ฌธ์ œ ์—†์Œ.

์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์ด๋‚˜ ์œ ์ง€๋ณด์ˆ˜? ์ธก๋ฉด์—์„œ๋Š” ์กด์žฌํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๊ธด ํ•˜๋‹ค๋งŒ...
๋‹ค๋ฅธ ์‚ฌ๋žŒ๊ณผ ํ˜‘์—…ํ•  ๋•Œ DTO๊ฐ€ ์žˆ์œผ๋ฉด ํŒŒ์•…์ด ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๋˜๋‹ˆ๊นŒ(๊ฐ€๋…์„ฑ) ๊ทธ๋Ÿฐ ๋ถ€๋ถ„์—์„œ๋„ ์ข‹๊ธด ํ•˜๋‹ค๋งŒ...

๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ์œผ๋กœ๋Š” 5๊ฐœ ์ด์ƒ์ด ์•„๋‹ˆ๋ผ๋ฉด ๊ทธ๋ƒฅ DTO ๋งŒ๋“ค์ง€ ์•Š๊ณ  @RequestParam์„ ์จ์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋‚˜์„ ๊ฒƒ ๊ฐ™๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ์—ฌ๊ธฐ์„œ 8.Postman์œผ๋กœ API ํ…Œ์ŠคํŠธ ๋‚ด์šฉ์„ ๋ง๋ถ™์ด์ž๋ฉด,

ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ๋•Œ @RequestParamํ˜•์‹์€

์‚ฌ์ง„๊ณผ ๊ฐ™์ด Params์— ๊ฐ’์„ ์ž„์˜๋กœ ๋„ฃ์–ด์ฃผ๊ณ  ์š”์ฒญํ•˜๋ฉด Body๋กœ 200 ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ณ 

@RequestBody TransferRatioDTO requestDTO ํ˜•์‹์€

์ด๋ ‡๊ฒŒ Body๋กœ raw ๊ฐ’์„ ์ž‘์„ฑํ•ด์„œ ์š”์ฒญํ•˜๋ฉด ์‚ฌ์ง„๊ณผ ๊ฐ™์ด ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
(Body > raw JSON ํ˜•์‹์œผ๋กœ ๊ฐ’์„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค.)

์•„์ง ๋ฐฑ๋ฆฐ์ด๋ผ ์ด๋Ÿฐ ๊ฒƒ๋„ ์„œ์„œํžˆ ์•Œ์•„๊ฐ€๋Š” ์ค‘...

3. Repository (JPA ์ธํ„ฐํŽ˜์ด์Šค) ์ž‘์„ฑ

AccountRepository

package org.team4.sol_server.domain.account.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.team4.sol_server.domain.account.entity.AccountEntity;

import java.util.Optional;

public interface AccountRepository extends JpaRepository<AccountEntity, Long> {
    Optional<AccountEntity> findByAccountNumber(String accountNumber);

}

AccountHistoryRepository

package org.team4.sol_server.domain.account.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.team4.sol_server.domain.account.entity.AccountHistoryEntity;

import java.util.List;

@Repository
public interface AccountHistoryRepository extends JpaRepository<AccountHistoryEntity, Integer> {
    // ํŠน์ • ๊ณ„์ขŒ์˜ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ
    List<AccountHistoryEntity> findByAccountAccountNumber(String accountNumber);
}
  • DB์™€ ์—ฐ๊ฒฐ๋˜๋Š” AccountRepository, AccountHistoryRepository ์ƒ์„ฑ

4. Service (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง) ๊ตฌํ˜„

package org.team4.sol_server.domain.account.service;

import lombok.RequiredArgsConstructor;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.team4.sol_server.domain.account.dto.AccountDTO;
import org.team4.sol_server.domain.account.entity.AccountEntity;
import org.team4.sol_server.domain.account.entity.AccountHistoryEntity;
import org.team4.sol_server.domain.account.repository.AccountHistoryRepository;
import org.team4.sol_server.domain.account.repository.AccountRepository;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class AccountService {
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ณ„์ขŒ ์กฐํšŒ, ์ž…๊ธˆ, ์ถœ๊ธˆ, ์ด์ฒด, ์ด์ฒด ๋น„์œจ ์„ค์ •, ์ด์ž ๊ณ„์‚ฐ ๋“ฑ์„ ์ฒ˜๋ฆฌ
    private final AccountRepository accountRepository;
    private final AccountHistoryRepository accountHistoryRepository;

    public Optional<AccountEntity> getAccountByNumber(String accountNumber) {
        return accountRepository.findByAccountNumber(accountNumber);
    }

    @Transactional
    public boolean transfer(String fromAccountNumber, String toAccountNumber, Long amount) {
        Optional<AccountEntity> fromAccountOpt = accountRepository.findByAccountNumber(fromAccountNumber); // ์ถœ๊ธˆ ๊ณ„์ขŒ
        Optional<AccountEntity> toAccountOpt = accountRepository.findByAccountNumber(toAccountNumber); // ์ž…๊ธˆ ๊ณ„์ขŒ

        if (fromAccountOpt.isPresent() && toAccountOpt.isPresent()) {
            AccountEntity fromAccount = fromAccountOpt.get();
            AccountEntity toAccount = toAccountOpt.get();

            if (fromAccount.getBalance() >= amount) {
                fromAccount.setBalance(fromAccount.getBalance() - amount);
                toAccount.setBalance(toAccount.getBalance() + amount);
                accountRepository.save(fromAccount);
                accountRepository.save(toAccount);
                return true;
            }
        }
        return false;
    }

    @Transactional
    public boolean deposit(String accountNumber, Long amount) {
        Optional<AccountEntity> accountOpt = accountRepository.findByAccountNumber(accountNumber);

        if (accountOpt.isPresent() && amount > 0) {
            AccountEntity account = accountOpt.get();

            account.setBalance(account.getBalance() + amount);  //  ์ž…๊ธˆ ๋กœ์ง
            accountRepository.save(account);
            return true;
        }
        return false;
    }

    // ํŒŒํ‚น ํ†ต์žฅ ํˆฌ์ž ๋น„์œจ ์„ค์ •
    @Transactional
    public void setTransferRatio(String accountNumber, int ratio) {
        Optional<AccountEntity> accountOpt = accountRepository.findByAccountNumber(accountNumber);
        accountOpt.ifPresent(account -> {
            account.setInvestorRatio(ratio);
            accountRepository.save(account);
        });
    }

    // ํŒŒํ‚น ํ†ต์žฅ ํˆฌ์ž ๋น„์œจ ์กฐํšŒ
    @Transactional
    public Integer getTransferRatio(String accountNumber) {
        Optional<AccountEntity> accountOpt = accountRepository.findByAccountNumber(accountNumber);
        return accountOpt.map(AccountEntity::getInvestorRatio).orElse(0);
    }

    // collectInterest() --> ํˆฌ์ž ๋น„์œจ์„ ๊ธฐ์ค€์œผ๋กœ ์ด์ž ๊ณ„์‚ฐ ๋ฐ ๊ณ„์ขŒ ์ž”์•ก ์ฆ๊ฐ€ ํ•จ์ˆ˜
    @Transactional
    public AccountDTO collectInterest(String accountNumber) {
        Optional<AccountEntity> accountOpt = accountRepository.findByAccountNumber(accountNumber);
        if (accountOpt.isPresent()) {
            AccountEntity account = accountOpt.get();
            // ์ด์ž ๊ณ„์‚ฐ = ์ž”์•ก * (์ด์ž์œจ/100.0)
            int interest = (int) (account.getBalance() * (account.getInterestRatio() / 100.0));
            account.setInterest(interest);
            account.setBalance(account.getBalance() + interest);
            accountRepository.save(account);

            return AccountDTO.builder()
                    .accountNumber(account.getAccountNumber())
                    .balance(account.getBalance())
                    .build();
        }
        return null;
    }

    // ๋ฐ›์„ ์ด์ž ์กฐํšŒ
    public int getInterest(String accountNumber) {
        Optional<AccountEntity> accountOpt = accountRepository.findByAccountNumber(accountNumber);
        return accountOpt.map(AccountEntity::getInterest).orElse(0);
    }

    // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ
    @Transactional(readOnly = true)
    public List<AccountHistoryEntity> getAccountHistory(String accountNumber) {
        return accountHistoryRepository.findByAccountAccountNumber(accountNumber);
    }

}
  • set-ratio, collect-interest, get-interest, history ๊ธฐ๋Šฅ ๊ตฌํ˜„

5. Controller (API ์š”์ฒญ ์ฒ˜๋ฆฌ) ๊ตฌํ˜„

package org.team4.sol_server.domain.account.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.team4.sol_server.domain.account.dto.AccountDTO;
import org.team4.sol_server.domain.account.dto.DepositRequestDTO;
import org.team4.sol_server.domain.account.dto.TransferRatioDTO;
import org.team4.sol_server.domain.account.dto.TransferRequestDTO;
import org.team4.sol_server.domain.account.entity.AccountEntity;
import org.team4.sol_server.domain.account.entity.AccountHistoryEntity;
import org.team4.sol_server.domain.account.repository.AccountRepository;
import org.team4.sol_server.domain.account.service.AccountService;

import java.util.List;
import java.util.Optional;

/*
ํŒŒ์ผ๋ช… : AccountController.java
์ƒ์„ฑ์ž : JDeok
๋‚  ์งœ  : 2025.03.05
์‹œ ๊ฐ„  : ์˜คํ›„ 02:14
๊ธฐ ๋Šฅ  : ๊ณ„์ขŒํŽ˜์ด์ง€
Params :
Return :
๋ณ€๊ฒฝ์‚ฌํ•ญ
     - 2025.03.05 : JDeok(์ตœ์ดˆ์ž‘์„ฑ)
*/
@RestController
@RequestMapping("api/account")
@RequiredArgsConstructor
@CrossOrigin(origins = "http://localhost:3000")
public class AccountController {

    private final AccountService accountService;
    private final AccountRepository accountRepository;

    // ๊ณ„์ขŒ ์กฐํšŒ ํŽ˜์ด์ง€
    @GetMapping("/balance")
    public ResponseEntity<AccountDTO> getAccount(@RequestParam String accountNumber, Model model) {
        Optional<AccountEntity> account = accountService.getAccountByNumber(accountNumber);
        account.ifPresent(value -> model.addAttribute("account", value));

        AccountDTO dto = AccountDTO.builder()
                .accountNumber(account.get().getAccountNumber())
                .balance(account.get().getBalance())
                .build();

        return ResponseEntity.ok().body(dto);
    }

//    ์ด์ฒด ํผ ํŽ˜์ด์ง€
//    @GetMapping("/transfer")
//    public String showTransferForm() {
//        return "transfer";
//    }

    // ์ด์ฒด ๊ธฐ๋Šฅ ์ˆ˜ํ–‰
    @PostMapping("/transfer")
    public String transfer(@RequestBody TransferRequestDTO transferRequestDTO, Model model) {
        boolean success = accountService.transfer(transferRequestDTO.getFromAccount()
                                                , transferRequestDTO.getToAccount()
                                                , transferRequestDTO.getAmount());
        model.addAttribute("success", success);
        return "transfer_result";
    }

    // ์ž…๊ธˆ (Deposit)
    @PostMapping("/deposit")
    public ResponseEntity<String> deposit(@RequestBody DepositRequestDTO requestDTO) {

        boolean success = accountService.deposit(requestDTO.getAccountNumber(), requestDTO.getAmount());
        if (success) {
            return ResponseEntity.ok("Deposit successful");
        }
        return ResponseEntity.badRequest().body("Deposit failed: Invalid account or amount");
    }

    // ์ฆ๊ถŒ ๊ณ„์ขŒ ์ด์ฒด ๋น„์œจ ์„ค์ •
    @PostMapping("/set-ratio")
    // accountNumber --> ๊ณ„์ขŒ ๋ฒˆํ˜ธ ๋ฐ›์Œ, ratio --> ์ด์ฒด ๋น„์œจ(%) ๋ฐ›์Œ
    public ResponseEntity<String> setTransferRatio(@RequestBody TransferRatioDTO requestDTO) {
        accountService.setTransferRatio(requestDTO.getAccountNumber(), requestDTO.getRatio()); // ํˆฌ์ž ๋น„์œจ ์—…๋ฐ์ดํŠธ
        return ResponseEntity.ok("์ฆ๊ถŒ ๊ณ„์ขŒ ์ด์ฒด ๋น„์œจ ์„ค์ •: " + requestDTO.getRatio());
    }

    // ์ฆ๊ถŒ ๊ณ„์ขŒ ์ด์ฒด ๋น„์œจ ์กฐํšŒ
    @GetMapping("/get-ratio")
    public Integer getTransferRatio(@RequestParam String accountNumber) {
        return accountService.getTransferRatio(accountNumber);
    }

    // ์ด์ž ๋ฐ›๊ธฐ
    @PostMapping("/collect-interest")
    public ResponseEntity<AccountDTO> collectInterest(@RequestParam String accountNumber) {
        AccountDTO updatedAccount = accountService.collectInterest(accountNumber); // ํˆฌ์ž ๋น„์œจ์„ ๊ธฐ์ค€์œผ๋กœ ์ด์ž ๊ณ„์‚ฐ ๋ฐ ๊ณ„์ขŒ ์ž”์•ก ์ฆ๊ฐ€ ํ•จ์ˆ˜
        return ResponseEntity.ok(updatedAccount);
    }

    // ์ด์ž ์ •๋ณด ์กฐํšŒ --> ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ›์„ ์ด์ž
    // ํ˜„์žฌ ์ž”์•ก ๊ธฐ์ค€์œผ๋กœ ๋ฐ›์„ ์ด์ž ๊ณ„์‚ฐ ํ›„ ๋ฐ˜ํ™˜
    @GetMapping("/get-interest")
    public ResponseEntity<Integer> getInterest(@RequestParam String accountNumber) {
        int interest = accountService.getInterest(accountNumber); // ์ด์ž ๋น„์œจ์— ๋”ฐ๋ผ ์ด์ž ๊ณ„์‚ฐ
        return ResponseEntity.ok(interest);
    }

    // ํ•ด๋‹น ๊ณ„์ขŒ ํ†ต์žฅ ๋‚ด์—ญ ์กฐํšŒ
    @GetMapping("/history")
    public ResponseEntity<List<AccountHistoryEntity>> getAccountHistory(@RequestParam String accountNumber) {
        List<AccountHistoryEntity> historyList = accountService.getAccountHistory(accountNumber);
        return ResponseEntity.ok(historyList);
    }

}
  • ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  AccountController ์ž‘์„ฑ

์—ฌ๊ธฐ์„œ ์ž ๊น ๋‹ค์‹œ ๊ฐœ๋… ์ •๋ฆฌ๋ฅผ ํ•˜์ž๋ฉด

โœ” ์ปจํŠธ๋กค๋Ÿฌ(Controller)
ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ๋ฐ›์Œ (@PostMapping, @GetMapping)
์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋น„์Šค(Service)๋กœ ์ „๋‹ฌ
์„œ๋น„์Šค์—์„œ ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜

โœ” ์„œ๋น„์Šค(Service)
์„œ๋น„์Šค๋Š” ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(์—…๋ฌด ๋กœ์ง)์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณณ์ด์•ผ!
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ’์„ ์ฝ์–ด์˜ค๊ฑฐ๋‚˜, ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๋ณ€๊ฒฝํ•˜๋Š” ๊ฑด ๋ชจ๋‘ ์„œ๋น„์Šค์—์„œ ์ฒ˜๋ฆฌํ•ด.
๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌ (DB์—์„œ ์กฐํšŒ/์ˆ˜์ •, ๊ณ„์‚ฐ, ๊ฒ€์ฆ ๋“ฑ)
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ง์ ‘ ํ†ต์‹ 
์—ฌ๋Ÿฌ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ

โœ” ์ปจํŠธ๋กค๋Ÿฌ์™€ ์„œ๋น„์Šค๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด?
์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•˜๊ณ , ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›€
์žฌ์‚ฌ์šฉ์„ฑ ์ฆ๊ฐ€ (๊ฐ™์€ ๋กœ์ง์„ ์—ฌ๋Ÿฌ API์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฌ์›€ (์„œ๋น„์Šค ๋‹จ์œ„ ํ…Œ์ŠคํŠธ, ์ปจํŠธ๋กค๋Ÿฌ Mock ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ)

๐Ÿš€ ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๋Š” ๋น„์œ 
โœ… ์ปจํŠธ๋กค๋Ÿฌ(Controller) = ์›จ์ดํ„ฐ ๐Ÿฝ
๐Ÿ‘‰ ์†๋‹˜(ํด๋ผ์ด์–ธํŠธ) ์ฃผ๋ฌธ์„ ๋ฐ›๊ณ , ์š”๋ฆฌ๋ฅผ ์ฃผ๋ฐฉ(์„œ๋น„์Šค)์—๊ฒŒ ์ „๋‹ฌ

โœ… ์„œ๋น„์Šค(Service) = ์ฃผ๋ฐฉ ๐Ÿณ
๐Ÿ‘‰ ์›จ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌํ•œ ์ฃผ๋ฌธ์„ ์ฒ˜๋ฆฌํ•ด์„œ ์š”๋ฆฌ๋ฅผ ์™„์„ฑ

โœ… Repository = ์ฐฝ๊ณ  ๐Ÿช
๐Ÿ‘‰ ์ฃผ๋ฐฉ(์„œ๋น„์Šค)์—์„œ ์žฌ๋ฃŒ(DB ๋ฐ์ดํ„ฐ)๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์ €์žฅ

โœ” ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ชจ๋“  ๊ฑธ ์ฒ˜๋ฆฌํ•˜๋ฉด? ๐Ÿคฏ (์›จ์ดํ„ฐ๊ฐ€ ์š”๋ฆฌ๊นŒ์ง€ ๋‹ค ํ•˜๋ ค๋ฉด ํž˜๋“ค๊ฒ ์ง€?)
โœ” ์ปจํŠธ๋กค๋Ÿฌ๋Š” "์š”์ฒญ์„ ๋ฐ›์•„์„œ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• "๋งŒ ํ•˜๊ณ , ์š”๋ฆฌ๋Š” "์„œ๋น„์Šค"์—์„œ ํ•ด์•ผ ํ•ด!

6. ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ (๋”๋ฏธ ๋ฐ์ดํ„ฐ) ์‚ฝ์ž…

  • DB์— ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    - ์ด๊ฑด GPT์”จํ•œํ…Œ ๋งŒ๋“ค์–ด ๋‹ฌ๋ผ๊ณ  ํ•˜๋ฉด ์‹œ๊ฐ„ ์ ˆ์•ฝ ๊ฐ€๋Šฅ!

7. JPA ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

AccountRepositoryTest

package org.team4.sol_server.repository;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.team4.sol_server.domain.account.entity.AccountEntity;
import org.team4.sol_server.domain.account.repository.AccountRepository;

import java.util.Optional;

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

@DataJpaTest
public class AccountRepositoryTest {

    @Autowired
    private AccountRepository accountRepository;

    @Test
    public void testFindByAccountNumber() {
        Optional<AccountEntity> account = accountRepository.findByAccountNumber("ACC123456");

        assertThat(account).isPresent();
        assertThat(account.get().getBalance()).isEqualTo(1000000);
    }
}

AccountServiceTests

package org.team4.sol_server.service;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import org.team4.sol_server.domain.account.dto.AccountDTO;
import org.team4.sol_server.domain.account.entity.AccountEntity;
import org.team4.sol_server.domain.account.repository.AccountRepository;
import org.team4.sol_server.domain.account.service.AccountService;

import java.util.Optional;

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

@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional
public class AccountServiceTests {

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private AccountService accountService;

    private String testAccountNumber = "ACC123";
    private String testAccountNumber2 = "ACC456";

    @BeforeEach
    void setUp() {
        AccountEntity account1 = AccountEntity.builder()
                .accountNumber(testAccountNumber)
                .userIdx(1)
                .balance(1000000L)
                .investorRatio(50)
                .interest(25000)
                .interestRatio(2.5)
                .build();
        accountRepository.save(account1);

        AccountEntity account2 = AccountEntity.builder()
                .accountNumber(testAccountNumber2)
                .userIdx(2)
                .balance(500000L)
                .investorRatio(30)
                .interest(0)
                .interestRatio(1.2)
                .build();
        accountRepository.save(account2);
    }

    @Test
    public void testGetAccount() {
        // ๊ณ„์ขŒ ์ •๋ณด ์กฐํšŒ ํ…Œ์ŠคํŠธ
        Optional<AccountEntity> accountOpt = accountService.getAccountByNumber(testAccountNumber);
        assertThat(accountOpt).isPresent();
        assertThat(accountOpt.get().getBalance()).isEqualTo(1000000);
        System.out.println("ํŒŒํ‚น ํ†ต์žฅ ์ž”์•ก: " + accountOpt.get().getBalance());
    }

    @Test
    public void testDeposit() {
        // ์ž…๊ธˆ ํ…Œ์ŠคํŠธ: 10๋งŒ์› ์ž…๊ธˆ
        boolean result = accountService.deposit(testAccountNumber, 100000L);
        assertThat(result).isTrue();

        // ๋ณ€๊ฒฝ๋œ ์ž”์•ก ํ™•์ธ
        Optional<AccountEntity> updatedAccount = accountService.getAccountByNumber(testAccountNumber);
        assertThat(updatedAccount).isPresent();
        assertThat(updatedAccount.get().getBalance()).isEqualTo(1100000);
        System.out.println("ํŒŒํ‚น ํ†ต์žฅ ์ž”์•ก: " + updatedAccount.get().getBalance());
    }

    @Test
    public void testTransfer() {
        // ๊ณ„์ขŒ ๊ฐ„ ์†ก๊ธˆ ํ…Œ์ŠคํŠธ (์ถœ๊ธˆ 10๋งŒ์›)
        boolean result = accountService.transfer(testAccountNumber, testAccountNumber2, 100000L);
        assertThat(result).isTrue();

        // ์ž”์•ก ํ™•์ธ
        Optional<AccountEntity> fromAccount = accountService.getAccountByNumber(testAccountNumber);
        Optional<AccountEntity> toAccount = accountService.getAccountByNumber(testAccountNumber2);

        assertThat(fromAccount).isPresent();
        assertThat(toAccount).isPresent();
        assertThat(fromAccount.get().getBalance()).isEqualTo(900000);
        assertThat(toAccount.get().getBalance()).isEqualTo(600000);

        System.out.println("ํŒŒํ‚น ํ†ต์žฅ ์ž”์•ก: " + fromAccount.get().getBalance());
        System.out.println("์ฆ๊ถŒ ๊ณ„์ขŒ ์ž”์•ก: " + toAccount.get().getBalance());
    }

    @Test
    public void testSetTransferRatio() {
        // ํˆฌ์ž ๋น„์œจ ๋ณ€๊ฒฝ ํ…Œ์ŠคํŠธ
        accountService.setTransferRatio(testAccountNumber, 80);
        Optional<AccountEntity> updatedAccount = accountService.getAccountByNumber(testAccountNumber);
        assertThat(updatedAccount).isPresent();
        assertThat(updatedAccount.get().getInvestorRatio()).isEqualTo(80);
        System.out.println("ํˆฌ์ž ๋น„์œจ ๋ณ€๊ฒฝ: " + updatedAccount.get().getInvestorRatio());
    }

    @Test
    public void testCollectInterest() {
        // ์ด์ž ๋ฐ›๊ธฐ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ
        AccountDTO updatedAccount = accountService.collectInterest(testAccountNumber);

        // ์ด์ž๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
        assertThat(updatedAccount).isNotNull();
        assertThat(updatedAccount.getBalance()).isEqualTo(1025000);  // 100๋งŒ์› + 2.5% (25000์›)
        System.out.println("์ด์ž ๋ฐ›๊ธฐ: " + updatedAccount.getBalance());
    }

    @Test
    public void testGetInterest() {
        // ๋ฐ›์„ ์ด์ž ์กฐํšŒ ํ…Œ์ŠคํŠธ
        int interest = accountService.getInterest(testAccountNumber);
        assertThat(interest).isEqualTo(25000);  // 100๋งŒ์› ร— 2.5% = 25000์›
        System.out.println(interest);
    }

}

8. Postman์œผ๋กœ API ํ…Œ์ŠคํŠธ

๋!

์ถ”๊ฐ€์ ์œผ๋กœ MockMvc๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Spring Boot ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.
๊ตณ์ด Postman์„ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๋Š” ๋ฐฉ๋ฒ•์ธ๋ฐ, ํฌ์ŠคํŠธ๋งจ์”จ๊ฐ€ UI๊ฐ€ ์ด์˜๊ณ  ํŽธํ•˜๋‹ˆ๊นŒ ๊ฑ ๊ทธ๊ฑฐ ์“ฐ๋Š” ๊ฒŒ ๋‚ซ๊ธด ํ•˜๋‹ค.

๋ฐฉ์‹์€ ๋Œ€์ถฉ

package org.team4.sol_server.domain.account.controller;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.team4.sol_server.domain.account.service.AccountService;

import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(AccountController.class)  // ํŠน์ • ์ปจํŠธ๋กค๋Ÿฌ๋งŒ ํ…Œ์ŠคํŠธ
public class AccountControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean  // ์„œ๋น„์Šค ๊ณ„์ธต์„ Mocking (์˜์กด์„ฑ ์ฃผ์ž…)
    private AccountService accountService;

    @Test
    public void testGetBalance() throws Exception {
        // ๊ฐ€์งœ ์„œ๋น„์Šค ์‘๋‹ต ์„ค์ •
        when(accountService.getInterest("ACC123")).thenReturn(5000);

        // GET ์š”์ฒญ์„ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ ๊ฒ€์ฆ
        mockMvc.perform(get("/api/account/get-interest")
                        .param("accountNumber", "ACC123"))  // ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •
                .andExpect(status().isOk())  // ์‘๋‹ต ์ฝ”๋“œ 200์ธ์ง€ ํ™•์ธ
                .andExpect(content().string("5000"));  // ์‘๋‹ต ๋‚ด์šฉ ๊ฒ€์ฆ
    }
}

์ด๋Ÿฌํ•˜๋‹ค.


์ผ๋‹จ์€ ๋๋‚ฌ์ง€๋งŒ, ์ถ”๊ฐ€์ ์œผ๋กœ ๋” ๊ตฌํ˜„ํ•  ์‚ฌํ•ญ์ด ๋Š˜์–ด๋‚  ๊ฒƒ ๊ฐ™๊ธด ํ•˜๋‹ค.
์šฐ์„ ์€ ํ”„๋ก ํŠธ๋ž‘ ๋ฐฑ ์—ฐ๋™์„ ์‹œ์ž‘ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•ด๋ณด๊ณ  UI๊นŒ์ง€ ๋””์ž์ธ ์‹ธ์•… ํ•  ๊ณ„ํš~~~

โœ” API ๊ฐœ๋ฐœํ•˜๋ฉด์„œ DTO ํ•„์š”์„ฑ์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์„ ํ•ด๋ด„
โœ” Postman์„ ํ™œ์šฉํ•ด API ํ…Œ์ŠคํŠธ ๋ฐ ๋ฌธ์„œํ™” ์ง„ํ–‰
โœ” ๊ณง ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ ์—ฐ๋™ ํ›„ UI๊นŒ์ง€ ์™„์„ฑํ•  ๊ณ„ํš!

๐Ÿ”ฅ ์•ž์œผ๋กœ๋„ ๊ณ„์† ์„ฑ์žฅํ•˜๋Š” ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ธฐ ์œ„ํ•ด ๋‹ฌ๋ ค๊ฐ„๋‹ค! ๐Ÿš€

profile
๋ฒจ๋กœ๊ทธ ์ซŒ ์žฌ๋ฐŒ๋„ค?

0๊ฐœ์˜ ๋Œ“๊ธ€