백엔드 개발자를 선택하고 나서
개발자에게 프론트는 js, 백은 Java를 선택해야(?)만 할정도로 한국 사회에서 가장 널리 보급되고 있는 것 같다.
프론트 | 백엔드 | |
웹 | html, css, js (React, Next) | Java, Java Spring, Spring boot // node.js |
모바일 | Flutter |
이외에도 많지만 대표적으로 기본이 되는 것들을 추리면 이렇게 되는 것 같다. 위 기술들을 바탕으로 여러 언어와 기술을 습득해가는 방식(?). 모바일은 들어본게 Flutter라...
JDK와 IDE 선택
JDK는 자바 개발 키트의 약자로 자바 어플리케이션 개발을 위한 기본 도구다.
JDK안에는 JDE(자바 개발 환경), JVM(자바 가상 머신) 등이 포함되어 JAVA를 통해 어플리케이션을 개발할 수 있게 해주는 플랫폼이라고 생각하면 된다.
JDK를 설치하면 사실상 메모장을 통해서도 자바 어플리케이션을 만들 수 있다. 그러나, 백지 위에 모든 코드를 손수 쓰며 코딩하기에는 생산성이 떨어지므로, IDE라는 통합개발툴을 사용한다.
많이 쓰는 Oracle의 open JDK를 설치하였으며, Intellij(인텔리제이)의 Ultimate 버젼을 사용.
WAS(TOMCAT)과 Web서버
Web서버와 WAS의 차이는 가장 대표적으로는 '어떤 요청을 처리하는가'다.
- Web서버는 html, css, js등 정적인 요청을 수행하는 역할.
- WAS는 DB에서 데이터를 가져오거나, API를 호출하는 등 동적인 요청을 해결하는 역할.
위 그림과 같이 일반적으로 클라이언트의 요청을 받아 서버는 연결된 어플리케이션(+데이터베이스)의 동작을 반환한다.
따라서 '어떻게 효율적으로 반환할 수 있지?'라는 생각을 바탕으로 개발을 해나가는 것이 백엔드 개발자의 좋은 자세라고 생각한다.
MVC
좋은 반환(?)의 첫번째는 MVC 패턴이며, Chat-gpt에 따르면, MVC의 정의는 아래와 같다.
동작에 따라 3가지 큰 분류를 통해 로직을 짜는 방법이다.
Controller가 클라이언트의 요청을 받고 Model에서 데이터를 추출해 View를 통해 반환한다.
MVC (Model-View-Controller):
- 목적: 사용자 인터페이스 및 비즈니스 로직을 구조화하고 분리하기 위한 디자인 패턴입니다.
- 역할:
- Model: 데이터와 비즈니스 로직을 담당합니다. 데이터의 상태를 유지하고 업데이트하는 역할을 합니다.
- View: 사용자에게 보이는 부분을 담당합니다. Model의 데이터를 시각적으로 표현하고 사용자와의 상호작용을 처리합니다.
- Controller: 사용자의 입력을 받아 Model과 View 사이에서 중개자 역할을 합니다. 입력을 기반으로 Model을 업데이트하고, View를 갱신합니다.
- 장점: 애플리케이션의 모듈화, 유지보수성, 확장성을 향상시킵니다.
*예시
@Controller
@RequestMapping("/example")
public class ExampleController {
// "/example/hello"에 대한 GET 요청을 처리하는 메서드
@GetMapping("/hello")
public String hello() {
return "helloPage";
}
// "/example/greet"에 대한 POST 요청을 처리하는 메서드
@PostMapping("/greet")
public String greet(@RequestParam String name, Model model) {
model.addAttribute("name", name);
return "greetPage";
}
}
@Controller라는 어노테이션을 통해 위의 클래스가 컨트롤러임을 지정한다.
@___Mapping은 요청에 따라 메서드를 정하고 return 값을 통해 반환될 View를 지정한다.
자세한 내용은 HTTP 정리 글에서 다룰 예정이므로 간략하게 Mapping의 종류만 정리한다.
@GetMapping("/example")
public String example() {
// ...
}
@PostMapping("/create")
public String create(@RequestBody DataObject data) {
// ...
}
@PutMapping("/update/{id}")
public String update(@PathVariable Long id, @RequestBody UpdatedDataObject updatedData) {
// ...
}
@DeleteMapping("/delete/{id}")
public String delete(@PathVariable Long id) {
// ...
}
API
백엔드 개발자의 주업무라고 해야하나.. 회원, 상품, 주문, 정산 등 API개발을 한다.
하나의 소프트웨어는 컨포넌트의 집합이라고 할 수 있다. 그리고 컨포넌트 간의 통신을 돕는 것이 API의 역할이다.
API (Application Programming Interface):
- 목적: 다른 소프트웨어 컴포넌트들이 상호작용하기 위한 인터페이스를 제공하는 도구 또는 규칙의 모음입니다.
- 역할: 서로 다른 소프트웨어 시스템이나 서비스 간에 통신하고 데이터를 교환할 수 있게 해줍니다. 주로 함수, 메서드, 클래스 등의 형태로 제공됩니다.
- 종류:
- Web API: HTTP/HTTPS를 통해 웹에서 서비스되는 API. REST 또는 SOAP을 사용할 수 있습니다.
- Library API: 라이브러리에서 제공되는 함수 호출 형태의 API.
- Operating System API: 운영체제에서 제공되는 기능에 접근하기 위한 API.
- 장점: 다양한 플랫폼 및 언어 간의 통합을 용이하게 하며, 모듈 간에 표준화된 방식으로 상호작용할 수 있도록 도와줍니다.
컴포넌트 역할과 동작:
- 서비스 (Service):
- 역할: 비즈니스 로직을 수행하는 부분으로, 특정 기능 또는 업무를 담당합니다.
- 동작: 주로 비즈니스 로직을 구현하고, 해당 기능에 필요한 데이터를 처리하기 위해 리포지토리를 호출합니다.
- 리포지토리 (Repository):
- 역할: 데이터베이스와 관련된 작업을 수행하며, 데이터에 접근하고 변경하는 역할을 합니다.
- 동작: 데이터의 CRUD(Create, Read, Update, Delete) 작업을 처리하고, 서비스에게 필요한 데이터를 제공합니다.
- 컨트롤러 (Controller):
- 역할: 사용자의 요청을 받고, 해당 요청에 대한 응답을 반환하는 부분입니다. 웹 애플리케이션에서 클라이언트와 상호작용합니다.
- 동작: HTTP 요청을 받아서 서비스에게 전달하고, 서비스로부터 받은 결과를 적절한 형태로 응답합니다.
*예시
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public void registerMember(Member member) {
// 비즈니스 로직 수행, 예를 들어 중복 회원 검사 등
if (isDuplicateMember(member)) {
throw new IllegalStateException("이미 가입된 회원입니다.");
}
memberRepository.save(member);
}
private boolean isDuplicateMember(Member member) {
// 중복 여부 검사 로직
}
}
@Repository
public class MemberRepository {
private final Map<Long, Member> store = new HashMap<>();
private long sequence = 0L;
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping("/members/{id}")
public String getMemberById(@PathVariable Long id, Model model) {
// 회원 조회 로직
Member member = memberService.getMemberById(id);
model.addAttribute("member", member);
return "memberDetail";
}
@PostMapping("/members/new")
public String registerNewMember(@ModelAttribute MemberForm form) {
// 회원 가입 로직
Member member = new Member();
member.setName(form.getName());
memberService.registerMember(member);
return "redirect:/members/" + member.getId();
}
}
ORM과 JPA 그리고 RDBMS
ORM -> JPA -> Spring Data JPA
-> 객체와 DB를 Mapping해서 SQL문 없이 반복적인 CRUD를 구현.
Spring Data JPA에서는 JpaRepository 인터페이스를 확장하여 Repository를 생성할 때 이미 기본적인 CRUD 메서드들을 제공받게 됩니다. 개발자는 이러한 메서드를 사용함으로써 별도의 쿼리 메서드 작성이나 엔터티 매니저를 직접 다루지 않아도 됩니다. Spring Data JPA는 이러한 구현체를 런타임에 자동으로 생성하여 빈으로 등록합니다.
JPA와 Spring Data JPA 차이
JPA 예시:
//엔터티 클래스 (Entity Class):
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters, setters, 기타 메서드
}
//JPA를 사용한 Repository:
import javax.persistence.EntityManager;
import java.util.List;
public class MemberRepository {
private final EntityManager em;
public MemberRepository(EntityManager em) {
this.em = em;
}
public void save(Member member) {
em.persist(member);
}
public Member findById(Long id) {
return em.find(Member.class, id);
}
public List<Member> findAll() {
return em.createQuery("SELECT m FROM Member m", Member.class)
.getResultList();
}
// 기타 필요한 메서드들
}
//엔터티 매니저 활용:
EntityManager em = // 엔터티 매니저 생성 (예: EntityManagerFactory로부터)
MemberRepository repository = new MemberRepository(em);
Member member = new Member();
member.setName("John Doe");
repository.save(member);
Member foundMember = repository.findById(member.getId());
List<Member> allMembers = repository.findAll();
Spring Data JPA 예시:
//엔터티는 동일
//Spring Data JPA Repository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Long> {
// JpaRepository에서 기본적인 CRUD 메서드들을 이미 제공받음
}
//Spring Data JPA 활용:
MemberRepository repository = // Spring이 자동으로 구현한 Repository 빈을 주입받음
Member member = new Member();
member.setName("John Doe");
repository.save(member);
Member foundMember = repository.findById(member.getId()).orElse(null);
List<Member> allMembers = repository.findAll();
@Bean 등록 - Config
@Configuration을 가진 클래스는 주로 @Bean을 등록해 DI(의존성 주입)을 한다.
@Bean을 활용하는 경우
- 의존성 주입 필요: 다른 빈이나 구성 요소에서 해당 서비스를 사용해야 할 때, 빈으로 등록하여 의존성 주입을 받을 수 있습니다.
- AOP(Aspect-Oriented Programming)를 활용: 스프링의 AOP를 사용하려면 빈으로 등록된 객체에 대해서만 AOP를 적용할 수 있습니다.
- 트랜잭션 관리: 서비스 메서드에 트랜잭션을 적용하려면 스프링의 트랜잭션 관리를 위해 빈으로 등록하는 것이 효과적입니다.
- 테스트 용이성: 빈으로 등록된 서비스는 테스트에서 쉽게 목 객체(Mock Object)로 대체하거나 스프링 테스트 컨텍스트를 활용하여 테스트할 수 있습니다.
- 컨트롤러에서 사용: 스프링 MVC에서 컨트롤러에서 서비스를 주입받아 사용할 때는 빈으로 등록하는 것이 일반적입니다.
Junit5
JUnit은 테스트 주도 개발(TDD) 및 소프트웨어 개발의 품질 향상을 위해 많이 사용되는 도구 중 하나로 단위 테스트를 통해 개발 코드를 리뷰한다.
*예시
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MyTest {
@Test
public void testAddition() {
assertEquals(4, 2 + 2);
}
}
'Java Spring > Spring Framework' 카테고리의 다른 글
롬복, 컴포넌트 스캔 ~ 의존 관계 주입 Jav Spring 프레임워크의 기본 (6) (0) | 2023.12.04 |
---|---|
스프링 컨테이너와 싱글톤 컨테이너 Jav Spring 프레임워크의 기본 (5) (1) | 2023.11.29 |
좋은 객체 지향 SOLID를 위한 config 구성창 Jav Spring 프레임워크의 기본 (4) (0) | 2023.11.28 |
회원 도메인 설계! Jav Spring 프레임워크의 기본 (3) (1) | 2023.11.27 |
JAVA는 객체 지향 프로그래밍? Jav Spring 프레임워크의 기본 (2) (0) | 2023.11.27 |