본문 바로가기

개인프로젝트/MOMO

[내가 만든 프로젝트 코드 분석 | MOMO] SpringMVC와 Mybatis 접근방식

https://www.youtube.com/watch?v=ty7ILcI-h0E

 

1. 스프링 구조

계층구조 앞단인 웹과의 통신 뒷단인 디비와의 통신

의존성 주입

객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라 한다.

생성자에 @Autowired를 사용하면 객체 생성 시점에 연관된 객체를 스프링 컨테이너에서 해당 스프링 빈을 찾아 서 주입한다.

외부에서 필요한 객체를 주입받기 때문에 애플리케이션을 Controller, Service, Repository로 나누어 기능을 분리하고 각 계층 간의 결합도를 낮추면서 유연하게 설계하는 계층형 아키텍처가 가능해진다.

의존성 주입을 사용하면 동일한 인터페이스나 추상 클래스에 여러 구현체를 주입할 수 있는 유연성을 얻을 수 있다. 이를 통해 다양한 상황에서 객체의 동작을 변경하거나 확장할 수 있다. 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다. 이는 확장에는 열려있고 수정,변경에는 닫혀있는 OCP원칙도 지켜진다.

@Component 를 포함하는 @Controller @Service @Repository 다음 애노테이션도 스프링 빈으로 자동 등록된다.

회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할 수 있게 의존관계 설정

이렇게 만들어진 계층구조에서 컨트롤러 서비스 리포지토리를 구분하고

웹 애플리케이션 계층구조

  • 컨트롤러: 웹 MVC의 컨트롤러 역할로 사용자의 요청이 컨트롤러로 전송되면 그 요청을 처리하고 필요한 데이터를 서비스 계층으로 전달합니다.
  • 서비스: 애플리케이션의 비즈니스 규칙이 담긴 핵심 영역으로 컨트롤러로부터 받은 요청을 처리하고 비즈니스 로직을 수행한 데이터를 리포지토리에 넘깁니다.
  • 리포지토리: 데이터베이스와의 상호작용을 담당하며 서비스계층에서 처리된 데이터를 DB에 저장하고 관리합니다.
  • 도메인: 비즈니스 개념을 담아 사용하는 객체 주로 데이터베이스에 저장하고 관리됨

비즈니스 계층이 웹과 요청, 응답으로 통신하는 부분과 데이터를 관리하기위해 DB와 통신하는 부분으로 나누어 설명

2.HTTP통신

MVC

Model View Controller MVC 패턴은 하나의 서블릿이나, JSP로 처리하던 것을 컨트롤러(Controller)와 뷰(View)라는 영역으로 서로 역할을 나눈 것을 말한다.

컨트롤러: HTTP 요청을 받아서 파라미터를 검증하고 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.(비즈니스 로직은 서비스가 맡게된다) 모델: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다.

: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다

MVC와 템플릿엔진

서버사이드 렌더링(Server-Side Rendering) 방식에서는, 클라이언트가 웹 서버에 요청을 보낼 때 서버에서 HTML 페이지를 완성된 상태로 생성하여 클라이언트에게 반환. 이를 위해 템플릿 엔진이 사용되며, 서버는 데이터와 HTML 템플릿을 결합하여 동적인 HTML 페이지를 만들어 클라이언트에게 전달하는 방식

뷰 리졸버(ViewResolver)의 기능은 클라이언트가 요청한 URL을 처리하고, 이를 바탕으로 적절한 뷰(View)를 찾아 렌더링하는 것이다. Spring MVC에서 컨트롤러 메서드가 반환하는 문자열(보통 뷰 이름)을 바탕으로, 뷰 리졸버는 해당 뷰를 찾아 응답으로 보내줄 HTML이나 JSP 페이지를 결정하는 역할을 한다.

최근에는 SPA(Single Page Application)와 같은 클라이언트 중심의 프레임워크(React, Vue, Angular 등)가 등장하면서, 템플릿 엔진의 역할은 점차 줄어들고 있다.

JSP를 사용하여 작업

웹 기술의 발전, 클라우드와 모바일 애플리케이션의 확산, 서비스 간 상호작용의 필요성 때문에 API개발 방식이 확장되고 있다.

API

클라이언트사이드 렌더링(Client-Side Rendering) 방식에서는, 클라이언트가 서버로부터 데이터만을 받아오고, HTML은 클라이언트 측에서 JavaScript를 이용해 동적으로 생성해. 서버는 HTML을 전송하지 않고 JSON과 같은 데이터만 제공하고, 클라이언트가 데이터를 받아 렌더링하는 방식이야. 이때 API 서버가 그 데이터를 제공하는 역할을 해.

@ResponseBody 문자 반환 @ResponseBody를 사용하면 뷰 리졸버( viewResolver)를 사용하지 않음

viewResolver 대신에 HttpMessageConverter가 동작 HTTP의 BODY에 문자 내용을 JSON 형식으로 직접 반환

3. 관계형 데이터베이스 관리

Mybatis와 JPA

MyBatis: MyBatis는 SQL을 기반으로 데이터베이스 작업을 직접적으로 처리한다. 개발자가 SQL 쿼리를 직접 작성해야 하며, 이 SQL 쿼리를 XML 파일이나 어노테이션을 통해 관리한다. MyBatis는 SQL이 중심에 있으며, 개발자가 데이터베이스와의 상호작용을 완벽하게 제어할 수 있다.

MyBatis의 가장 매력적인 점은 SQL을 XML에 편리하게 작성할 수 있고 또 동적 쿼리를 매우 편리하게 작성할 수 있다는 점이다.

JPA: JPA는 ORM(Object-Relational Mapping) 프레임워크로, 객체 지향적으로 데이터를 다룬다. SQL을 직접 작성하지 않고, 객체를 정의하면 JPA가 해당 객체를 데이터베이스의 테이블과 자동으로 매핑해준다. 내부적으로 쿼리는 JPA가 자동으로 생성하며, 필요시 JPQL(Java Persistence Query Language)이라는 객체 지향적 쿼리 언어를 사용할 수 있다.

동적쿼리

동적 쿼리는 실행 시점에서 입력된 조건이나 값에 따라 쿼리가 변경될 필요가 있는 경우 사용한다.

프로젝트에서는 제목이나 모임장을 조건으로 모임을 조회할 때 사용되었다.

동적쿼리가 없다면 각각의 조건마다 쿼리문을 작성하거나 문자열을 조립해서 쿼리문을 실행해야한다.

동적쿼리문으로 작성하면 쿼리문의 WHERE절에 조건에 따라 결과가 다른 SELECT문이 실행 되어야 하는 상황에서 하나의 쿼리로 다양한 조건을 처리할 수 있어서 중복을 줄이고 유지보수가 쉬워 진다.

public List<Mmaker> getMmakers(String searchKeywordType, String searchKeyword);
<select id="getMmakers" resultType="Mmaker">
		SELECT A.*, M.name AS writerName
				FROM SB_AM.mmaker AS A
				INNER JOIN SB_AM.member AS M
				ON A.memberId = M.id
				<if test="searchKeyword != ''">
					<choose>
						<when test="searchKeywordType == 'moimMain'">
							AND A.moimMain LIKE CONCAT('%', #{searchKeyword}, '%')
						</when>
						<when test="searchKeywordType == 'writeName'">
							AND M.name LIKE CONCAT('%', #{searchKeyword}, '%')
						</when>
					</choose>
				</if>
			    ORDER BY id DESC
	
	</select>

1. 개념 정리

동적 쿼리는 SQL 쿼리의 조건이나 구조를 실행 시점에 동적으로 변경하기위한 기본 문법을 알아보자.

마이바티스(MyBatis)는 XML 파일 안에서 SQL 쿼리를 동적으로 생성할 수 있게 해주는 다양한 태그를 사용하여 구성된다. 이 태그들을 통해 조건에 따라 SQL 쿼리의 일부를 추가하거나 제거할 수 있다.

  • <if>: 특정 조건이 참일 경우에만 해당 SQL 구문을 포함.
  • <choose>, <when>, <otherwise>: 여러 조건 중 하나를 선택하여 실행. Java의 switch-case 문과 유사.
  • <where>: 조건문을 처리하여 WHERE 절을 동적으로 생성.
  • <trim>: 필요에 따라 쿼리 앞뒤의 불필요한 부분을 제거.
  • <set>: UPDATE 쿼리에서 SET 절을 처리하며, 마지막에 오는 불필요한 쉼표(,)를 자동으로 제거.
  • <foreach>: 리스트나 배열을 반복 처리하여 쿼리를 동적으로 생성.

MyBatis의 동적 쿼리를 활용한 핵심 부분이다.

  • <if test="searchKeyword != ''">:
    • searchKeyword 값이 빈 문자열이 아닐 경우에만 조건문을 추가한다. 즉, 검색 키워드가 있을 때만 검색 조건을 실행한다.
  • <choose>:
    • *<choose>*는 여러 조건 중 하나만 선택하여 실행하는 제어 구조다. switch 문과 유사하다.
    • <when test="searchKeywordType == 'moimMain'">:
      • searchKeywordType이 'moimMain'일 경우, A.moimMain 컬럼(모임 제목)에서 검색 키워드를 포함하는 레코드를 찾는다. LIKE 연산자를 사용하여 부분 문자열 검색을 수행하며, CONCAT('%', #{searchKeyword}, '%')는 키워드 앞뒤에 %를 추가하여 부분 일치 검색을 가능하게 한다.
    • <when test="searchKeywordType == 'writeName'">:
      • searchKeywordType이 'writeName'일 경우, 작성자의 이름(M.name)에서 검색 키워드를 포함하는 레코드를 찾는다.
    이 choose 문 덕분에 하나의 쿼리에서 다양한 검색 조건을 동적으로 처리할 수 있다. 검색 타입에 따라 다른 조건을 적용하기 때문에 효율적이다.