ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๐Ÿ“Œ ๋„๋ฉ”์ธ ๋ถ„์„ ์„ค๊ณ„

0๏ธโƒฃ ์š”๊ตฌ์‚ฌํ•ญ ๋ถ„์„

  - ์‹ค์ œ ๋™์ž‘ํ•˜๋Š” ํ™”๋ฉด ํ™•์ธ

  - ๊ธฐ๋Šฅ ๋ชฉ๋ก ํ™•์ธ

 

1๏ธโƒฃ ๋„๋ฉ”์ธ ๋ชจ๋ธ๊ณผ ํ…Œ์ด๋ธ” ์„ค๊ณ„

 

- ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„(ex. ์ƒํ’ˆ๊ณผ ์ฃผ๋ฌธ)๋Š” ์ผ๋Œ€๋‹ค(ex. ์ฃผ๋ฌธ๊ณผ ์ฃผ๋ฌธ์ƒํ’ˆ), ๋‹ค๋Œ€์ผ(ex. ์ฃผ๋ฌธ์ƒํ’ˆ๊ณผ ์ƒํ’ˆ) ๊ด€๊ณ„๋กœ ํ’€๊ธฐ

- ์นดํ…Œ๊ณ ๋ฆฌ๋Š” parent, child๋กœ ๋ถ€๋ชจ, ์ž์‹ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ!

- ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๋ฆฌ(์†Œ์œ )ํ•˜๊ณ  ์žˆ์œผ๋ฉด ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ž„

- ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์—์„œ '๋‹ค'๊ฐ€ ๋ณดํ†ต ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ!

 

2๏ธโƒฃ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ๊ฐœ๋ฐœ

- ์ด๋ก ์ ์œผ๋ก  Getter, Setter ๋ชจ๋‘ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ , ๊ผญ ํ•„์š”ํ•œ ๋ณ„๋„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋Š”๊ฒŒ ๊ฐ€์žฅ ์ด์ƒ์ !

- but ์‹ค๋ฌด์—์„œ๋Š” Getter๋ฅผ ๋ชจ๋‘ ์—ด์–ด๋‘๊ณ  Setter ๋Œ€์‹ ์— ๋ณ€๊ฒฝ ์ง€์ ์ด ๋ช…ํ™•ํ•˜๋„๋ก ๋ณ€๊ฒฝ์„ ์œ„ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ๋ฅผ ๋ณ„๋„๋กœ ์ œ๊ณต

@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    
    private String name;
    
    @Embedded
    private Address address;
    
    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>(); //์ปฌ๋ ‰์…˜์€ ํ•„๋“œ์—์„œ ์ดˆ๊ธฐํ™”
}

- ์ปฌ๋ ‰์…˜์€ ํ•„๋“œ์—์„œ ์ดˆ๊ธฐํ™”

@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {

    @Id @GeneratedValue
    @Column(name = "order_id")
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY) //๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ, ํ•„์š”ํ•œ ์‹œ์ ์— ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ด!
    @JoinColumn(name = "member_id") 
    private Member member; //์ฃผ๋ฌธ ํšŒ์›
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();
    
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
    @JoinColumn(name = "delivery_id")
    private Delivery delivery; //๋ฐฐ์†ก์ •๋ณด

    private LocalDateTime orderDate; //์ฃผ๋ฌธ์‹œ๊ฐ„ 
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status; //์ฃผ๋ฌธ์ƒํƒœ [ORDER, CANCEL]
    
    //==์—ฐ๊ด€๊ด€๊ณ„ ๋ฉ”์„œ๋“œ==//
    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }
    
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
    
    public void setDelivery(Delivery delivery) {
        this.delivery = delivery;
        delivery.setOrder(this);
    }
    
    //==์ƒ์„ฑ ๋ฉ”์„œ๋“œ==//
    public static Order createOrder(Member member, Delivery delivery,
    OrderItem... orderItems) {
        Order order = new Order();
        order.setMember(member);
        order.setDelivery(delivery);
        for (OrderItem orderItem : orderItems) {
            order.addOrderItem(orderItem);
        }
        order.setStatus(OrderStatus.ORDER);
        order.setOrderDate(LocalDateTime.now());
        return order;
    }
    
    //==๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง==//
    /** ์ฃผ๋ฌธ ์ทจ์†Œ */
    public void cancel() {
        if (delivery.getStatus() == DeliveryStatus.COMP) {
            throw new IllegalStateException("์ด๋ฏธ ๋ฐฐ์†ก์™„๋ฃŒ๋œ ์ƒํ’ˆ์€ ์ทจ์†Œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."); 
        }
        
        this.setStatus(OrderStatus.CANCEL);
        for (OrderItem orderItem : orderItems) {
            orderItem.cancel();
        }
    }

    //==์กฐํšŒ ๋กœ์ง==//
    /**์ „์ฒด ์ฃผ๋ฌธ ๊ฐ€๊ฒฉ ์กฐํšŒ*/ 
    public int getTotalPrice() {
        int totalPrice = 0;
        for (OrderItem orderItem : orderItems) {
            totalPrice += orderItem.getTotalPrice();
        }
        return totalPrice;
    }
}

- ๋ชจ๋“  ์—ฐ๊ด€๊ด€๊ณ„๋Š” ์ง€์—ฐ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •(fetch = FetchType.LAZY)

- cascade = CascadeType.ALL๋กœ ํ•˜๋ฉด ์—ฐ๊ด€๊ด€๊ณ„์— ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ชจ๋‘ persist(์˜์†)ํ•ด์คŒ

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "item_id")
    private Long id;
    
    private String name;
    private int price;
    private int stockQuantity;
    
    @ManyToMany(mappedBy = "items") 
    private List<Category> categories = new ArrayList<Category>();
    
    //==๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง==//
    public void addStock(int quantity) {
        this.stockQuantity += quantity;
    }
    
    public void removeStock(int quantity) {
        int restStock = this.stockQuantity - quantity;
        if (restStock < 0) {
            throw new NotEnoughStockException("need more stock");
        }
        this.stockQuantity = restStock;
    }
}

- ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์—”ํ‹ฐํ‹ฐ์— ์œ„์ž„!(= ๋„๋ฉ”์ธ ๋ชจ๋ธ ํŒจํ„ด, ๊ฐ์ฒด ์ง€ํ–ฅ์˜ ํŠน์„ฑ↑)

@Entity
@DiscriminatorValue("B")
@Getter @Setter
public class Book extends Item {

    private String author;
    private String isbn;
    
}
@Entity
@Getter @Setter
public class Category {
    
    @Id @GeneratedValue
    @Column(name = "category_id")
    private Long id;
    
    private String name;
    
    @ManyToMany
    @JoinTable(name = "category_item",
            joinColumns = @JoinColumn(name = "category_id"),
            inverseJoinColumns = @JoinColumn(name = "item_id"))
    private List<Item> items = new ArrayList<>();
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Category parent;
    
    @OneToMany(mappedBy = "parent")
    private List<Category> child = new ArrayList<>();
    
    //==์—ฐ๊ด€๊ด€๊ณ„ ๋ฉ”์„œ๋“œ==//
    public void addChildCategory(Category child) {
        this.child.add(child);
        child.setParent(this);
    }
}

- ์‹ค๋ฌด์—์„œ๋Š” @ManyToMany๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง์ž!

@Embeddable
@Getter
public class Address {
    
    private String city;
    private String street;
    private String zipcode;
    
    protected Address() {
    }
    
    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    } 
}

- ๊ฐ’ ํƒ€์ž…์€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค๊ณ„!

- @Setter๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์ž๋ฐ” ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ protected๋กœ ์„ค์ •!

 

 

๐Ÿ“Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌํ˜„ ์ค€๋น„

0๏ธโƒฃ ๊ตฌํ˜„ ์š”๊ตฌ์‚ฌํ•ญ ๋ถ„์„

1๏ธโƒฃ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„

๊ณ„์ธตํ˜• ๊ตฌ์กฐ

- controller: ์›น ๊ณ„์ธต

- service: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง, ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ

- repository: JPA๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ, ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์‚ฌ์šฉ

- domain: ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ชจ์—ฌ ์žˆ๋Š” ๊ณ„์ธต, ๋ชจ๋“  ๊ณ„์ธต์—์„œ ์‚ฌ์šฉ

 

ํŒจํ‚ค์ง€ ๊ตฌ์กฐ

- jpabook.jpashop

        - domain

        - exception

        - repository

        - service

        - controller(web)

 

โ—๋„๋ฉ”์ธ ๊ฐœ๋ฐœ → ์„œ๋น„์Šค, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ณ„์ธต ๊ฐœ๋ฐœ → ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ ๋ฐ ๊ฒ€์ฆ → ์›น ๊ณ„์ธต(์ปจํŠธ๋กค๋Ÿฌ) ์ ์šฉ!

 

2๏ธโƒฃ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ฐœ๋ฐœ

@Repository
public class MemberRepository {
    
    @PersistenceContext
    private EntityManager em;
    
    public void save(Member member) {
        em.persist(member);
    }
    
    public Member findOne(Long id) {
        return em.find(Member.class, id);
    }
    
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
    
    public List<Member> findByName(String name) {
        return em.createQuery("select m from Member m where m.name = :name", Member.class)
                .setParameter("name", name)
                .getResultList();
    }
}

- ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ↓ ์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ!

@Repository
@RequiredArgsConstructor
public class MemberRepository {
    private final EntityManager em;
    ... 
}

 

3๏ธโƒฃ ์„œ๋น„์Šค ๊ฐœ๋ฐœ

@Service
@Transactional(readOnly = true
@RequiredArgsConstructor
public class MemberService {
    
    private final MemberRepository memberRepository;
    
    /**
     * ํšŒ์›๊ฐ€์ž…
     */
    @Transactional //๋ณ€๊ฒฝ
    public Long join(Member member) { 
    
        validateDuplicateMember(member); //์ค‘๋ณต ํšŒ์› ๊ฒ€์ฆ
        memberRepository.save(member);
        return member.getId();
    }
      
    private void validateDuplicateMember(Member member) {
        List<Member> findMembers = memberRepository.findByName(member.getName());
        if (!findMembers.isEmpty()) {
            throw new IllegalStateException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); 
        }
    }
    
    /**
     * ์ „์ฒด ํšŒ์› ์กฐํšŒ
     */
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }
    
    public Member findOne(Long memberId) {
        return memberRepository.findOne(memberId);
    }
}

- ์Šคํ”„๋ง ํ•„๋“œ ์ฃผ์ž… ๋ณด๋‹ค๋Š” ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜์ž!

 

4๏ธโƒฃ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ

@RunWith(SpringRunner.class) //์Šคํ”„๋ง๊ณผ ํ…Œ์ŠคํŠธ ํ†ตํ•ฉ
@SpringBootTest //์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ๋„์šฐ๊ณ  ํ…Œ์ŠคํŠธ
@Transactional //๊ฐ๊ฐ์˜ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ  ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๊ฐ•์ œ๋กœ ๋กค๋ฐฑ 
public class MemberServiceTest {
    
    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;

    @Test
    public void ํšŒ์›๊ฐ€์ž…() throws Exception {
    
        //Given
        Member member = new Member();
        member.setName("kim");
        
        //When
        Long saveId = memberService.join(member);
        
        //Then
        assertEquals(member, memberRepository.findOne(saveId));
    }
    
    @Test(expected = IllegalStateException.class) 
    public void ์ค‘๋ณต_ํšŒ์›_์˜ˆ์™ธ() throws Exception {
        //Given
        Member member1 = new Member();
        member1.setName("kim");
        
        Member member2 = new Member();
        member2.setName("kim");

        //When
        memberService.join(member1); 
        memberService.join(member2); //์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•œ๋‹ค.
    
        //Then
        fail("์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•œ๋‹ค."); 
    }
}

- ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์œ„ํ•œ ์Šคํ”„๋ง ํ™˜๊ฒฝ๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋Š” ํ™˜๊ฒฝ์€ ๋ณดํ†ต ๋‹ค๋ฅด๋ฏ€๋กœ ์„ค์ • ํŒŒ์ผ์„ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜์ž! (test/resources/application.yml)

 

โœ”๏ธ ์ฃผ๋ฌธ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ(๋™์  ์ฟผ๋ฆฌ)

1๏ธโƒฃ JPQL

2๏ธโƒฃ JPA Criteria

3๏ธโƒฃ Querydsl

 

 

๐Ÿ“Œ ์›น ๊ณ„์ธต ๊ฐœ๋ฐœ

โ—ํผ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ™”๋ฉด ๊ณ„์ธต๊ณผ ์„œ๋น„์Šค ๊ณ„์ธต์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ!

  - ์‹ค๋ฌด์—์„œ ์—”ํ‹ฐํ‹ฐ๋Š” ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ํ™”๋ฉด์„ ์œ„ํ•œ ๋กœ์ง์€ ์—†์–ด์•ผ ํ•จ! ํ™”๋ฉด์ด๋‚˜ API์— ๋งž๋Š” ํผ ๊ฐ์ฒด๋‚˜ DTO๋ฅผ ์‚ฌ์šฉํ•˜์ž! ๊ทธ๋ž˜์„œ ํ™”๋ฉด์ด๋‚˜ API ์š”๊ตฌ์‚ฌํ•ญ์„ ์ด๊ฒƒ๋“ค๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์—”ํ‹ฐํ‹ฐ๋Š” ์ตœ๋Œ€ํ•œ ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์œ ์ง€ํ•˜์ž!

@Getter @Setter
public class MemberForm {
    
    @NotEmpty(message = "ํšŒ์› ์ด๋ฆ„์€ ํ•„์ˆ˜ ์ž…๋‹ˆ๋‹ค") 
    private String name;
    
    private String city;
    private String street;
    private String zipcode;
}

0๏ธโƒฃ ์ปจํŠธ๋กค๋Ÿฌ ๊ฐœ๋ฐœ

@Controller
@RequiredArgsConstructor
public class MemberController {
    
    private final MemberService memberService;
    
    @GetMapping(value = "/members/new")
    public String createForm(Model model) {
        model.addAttribute("memberForm", new MemberForm());
        return "members/createMemberForm";
    }
    
    @PostMapping(value = "/members/new")
    public String create(@Valid MemberForm form, BindingResult result) {
        
        if (result.hasErrors()) {
            return "members/createMemberForm";
        }
        
        Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
        Member member = new Member();
        member.setName(form.getName());
        member.setAddress(address);
        
        memberService.join(member);
        return "redirect:/";
    }
    
    @GetMapping(value = "/members") 
    public String list(Model model) {
        List<Member> members = memberService.findMembers();
        model.addAttribute("members", members);
        return "members/memberList";
    }
}

 

 

๐Ÿ“Œ ๋ณ€๊ฒฝ ๊ฐ์ง€์™€ ๋ณ‘ํ•ฉ(merge)

๐Ÿ™‹๐Ÿป‍โ™€๏ธ ์ค€์˜์† ์—”ํ‹ฐํ‹ฐ?

: ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(entity manager)๊ฐ€ ๋”๋Š” ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š” ์—”ํ‹ฐํ‹ฐ. ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•ด๋„ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๊ธฐ๋Šฅ ๋™์ž‘X

  ๋”ฐ๋ผ์„œ, 1๏ธโƒฃ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ 2๏ธโƒฃ ๋ณ‘ํ•ฉ(merge)์„ ์‚ฌ์šฉํ•ด์„œ ์ค€์˜์† ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •

 

 1๏ธโƒฃ ๋ณ€๊ฒฝ ๊ฐ์ง€

@Transactional
void update(Item itemParam) { //itemParam: ํŒŒ๋ฆฌ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์ค€์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ
    Item findItem = em.find(Item.class, itemParam.getId()); //๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ
    findItem.setPrice(itemParam.getPrice()); //๋ฐ์ดํ„ฐ ์ˆ˜์ •
}

- ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ  ๋‹ค์‹œ ์กฐํšŒํ•œ ํ›„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •

 

2๏ธโƒฃ ๋ณ‘ํ•ฉ(merge)

@Transactional
void update(Item itemParam) { //itemParam: ํŒŒ๋ฆฌ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์ค€์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ 
    Item mergeItem = em.merge(itemParam);
}

โ—๋ณ‘ํ•ฉ ์‹œ ๋ชจ๋“  ์†์„ฑ์ด ๋ณ€๊ฒฝ๋จ, ๊ฐ’์ด ์—†์œผ๋ฉด null๋กœ ์—…๋ฐ์ดํŠธ (← ๋ณ‘ํ•ฉ์„ ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•˜๋Š” ์ด์œ )

@Repository
public class ItemRepository {
    @PersistenceContext
    EntityManager em;
        
    public void save(Item item) {
        if (item.getId() == null) {
            em.persist(item);
        } else {
            em.merge(item);
        }
    }
    //...
}

↑ ์ƒˆ๋กœ์šด ์—”ํ‹ฐํ‹ฐ ์ €์žฅ๊ณผ ์ค€์˜์† ์—”ํ‹ฐํ‹ฐ ๋ณ‘ํ•ฉ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌ! (๊ทธ๋ž˜๋„ ๋ณ‘ํ•ฉ์€ ์‚ฌ์šฉํ•˜์ง€ ๋ง์ž...)

 

โœ”๏ธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์–ด์„คํ”„๊ฒŒ ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑX

โœ”๏ธ ์„œ๋น„์Šค ๊ณ„์ธต์— ์‹๋ณ„์ž(id)์™€ ๋ณ€๊ฒฝํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ „๋‹ฌ(ํŒŒ๋ผ๋ฏธํ„ฐ or DTO)

โœ”๏ธ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํ™”ํ•˜๊ณ  ์—”ํ‹ฐํ‹ฐ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝ! (์„œ๋น„์Šค ๊ณ„์ธต์— ํŠธ๋žœ์žญ์…˜์ด ์žˆ๊ธฐ๋•Œ๋ฌธ!)

โœ”๏ธ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ์ ์— ๋ณ€๊ฒฝ ๊ฐ์ง€๊ฐ€ ์‹คํ–‰

 

 

๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2025/07   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
๊ธ€ ๋ณด๊ด€ํ•จ