멀티캠퍼스/주차별 정리

[26.06.29-26.07.03] TIL 14주차 정리

buckwheat 2026. 7. 5. 15:07

Spring MVC부터 Spring Boot JDBC까지 한 주 정리

이번 주에는 Spring MVC 프로젝트에 공통 처리 기능을 붙이고, 파일 업로드와 REST API를 만든 뒤 Spring Boot 방식으로 넘어가는 흐름을 학습했다.

정리 범위는 다음 프로젝트다.

  • SpringMVC03_filter_aop_login_intercepter_transaction_encoding
  • SpringMVC04_file
  • SpringMVC05_REST
  • StartBoot01
  • StartBoot02

1. Spring MVC 기본 흐름

Spring MVC는 사용자의 요청을 Controller가 받고, 필요한 로직은 Biz(Service)Dao를 거쳐 처리한 뒤 JSP 화면으로 응답하는 구조다.

브라우저 요청
 -> DispatcherServlet
 -> Controller
 -> Biz(Service)
 -> Dao
 -> DB
 -> JSP 응답

목록 조회에서는 DB에서 가져온 데이터를 Model에 담아 JSP로 넘긴다.

@RequestMapping("/list.do")
public String selectList(Model model) {
    model.addAttribute("list", biz.selectList());
    return "mvclist";
}

Model은 Controller에서 JSP로 데이터를 전달할 때 사용한다.
위 코드에서는 게시글 목록을 list라는 이름으로 JSP에 넘긴다.

등록, 수정, 삭제 후에는 보통 redirect를 사용한다.

@RequestMapping("/insertres.do")
public String insert(BoardDto dto) {
    int res = biz.insert(dto);

    if (res > 0) {
        return "redirect:list.do";
    } else {
        return "redirect:insertform.do";
    }
}

redirect는 서버 내부 이동이 아니라 브라우저에게 다시 요청할 주소를 알려주는 방식이다.


2. Filter

Filter는 Controller에 도착하기 전에 먼저 실행되는 공통 처리 단계다.
이번 프로젝트에서는 한글 깨짐 방지와 요청 로그 출력에 사용했다.

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

forceEncoding=true는 요청과 응답에 UTF-8 인코딩을 강제로 적용하겠다는 의미다.

직접 만든 LogFilter에서는 요청 URI, URL, 브라우저 정보 등을 로그로 남겼다.

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) request;

    String uri = req.getRequestURI();
    String agent = req.getHeader("User-Agent");

    logger.info("URI: " + uri);
    logger.info("agent: " + agent);

    chain.doFilter(req, response);
}

chain.doFilter()를 호출해야 다음 단계인 DispatcherServlet과 Controller로 요청이 계속 이동한다.


3. AOP

AOP는 여러 곳에서 반복되는 공통 기능을 따로 분리하는 방식이다.
대표적으로 로그 출력, 트랜잭션 처리, 예외 처리 같은 기능에 사용한다.

핵심 로직: 게시글 등록, 수정, 삭제
공통 로직: 로그, 트랜잭션, 예외 처리

모든 메서드에 로그 코드를 직접 작성하면 코드가 지저분해진다.
AOP를 사용하면 핵심 로직은 그대로 두고, 공통 로직만 별도 클래스로 관리할 수 있다.


4. 로그인과 비밀번호 암호화

회원가입 시 비밀번호를 그대로 DB에 저장하지 않고 BCryptPasswordEncoder로 암호화했다.

String encode = passwordEncoder.encode(dto.getMemberpw());
dto.setMemberpw(encode);

int res = biz.insert(dto);

로그인할 때는 사용자가 입력한 비밀번호와 DB에 저장된 암호화 비밀번호를 비교한다.

if (res != null) {
    if (passwordEncoder.matches(dto.getMemberpw(), res.getMemberpw())) {
        session.setAttribute("login", res);
    }
}

matches()는 평문 비밀번호와 암호화된 비밀번호가 같은 값인지 확인해준다.


5. Interceptor

Interceptor는 Spring MVC 안에서 Controller 실행 전후에 동작한다.
이번 프로젝트에서는 로그인 여부를 확인하는 데 사용했다.

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws IOException {

    if (request.getSession().getAttribute("login") != null) {
        return true;
    }

    response.sendRedirect("loginform.do");
    return false;
}

preHandle()에서 true를 반환하면 Controller가 실행되고, false를 반환하면 Controller로 가지 않는다.

Filter와 Interceptor의 차이는 다음과 같이 볼 수 있다.

Filter: Spring MVC 바깥에서 먼저 처리
Interceptor: Spring MVC 안에서 Controller 전후 처리

6. Transaction

트랜잭션은 여러 DB 작업을 하나의 작업 단위로 묶는 개념이다.

@Service
@Transactional
public class BoardBizImpl implements BoardBiz {
    ...
}

DB 작업이 모두 성공하면 commit, 중간에 실패하면 rollback된다.

모두 성공 -> commit
중간 실패 -> rollback

예를 들어 insert 작업을 여러 번 수행하는 중 하나가 실패하면, 앞에서 성공한 작업까지 취소해서 데이터가 애매하게 남지 않도록 한다.


7. 파일 업로드

파일 업로드에서는 일반 form 전송과 다르게 multipart/form-data 설정이 필요하다.

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="mpfile">
</form>

Controller에서는 MultipartFile로 파일을 받는다.

@RequestMapping("/upload")
public String fileUpload(UploadFile uploadFile, BindingResult result, Model model) {
    fileValidator.validate(uploadFile, result);

    if (result.hasErrors()) {
        return "uploadform";
    }

    MultipartFile file = uploadFile.getMpfile();

    UploadFile fileobj = new UploadFile();
    fileobj.setFilename(file.getOriginalFilename());
    fileobj.setDesc(uploadFile.getDesc());

    model.addAttribute("fileobj", fileobj);
    return "uploadres";
}

파일을 서버 폴더에 저장할 때는 InputStreamOutputStream을 사용했다.

inputStream = file.getInputStream();
outputStream = new FileOutputStream(newFile);

byte[] b = new byte[(int) file.getSize()];

while ((res = inputStream.read(b)) != -1) {
    outputStream.write(b, 0, res);
}

파일 업로드 흐름은 다음과 같다.

파일 선택
 -> multipart/form-data 전송
 -> MultipartFile로 받기
 -> 유효성 검사
 -> 서버 폴더에 저장
 -> 결과 화면 출력

8. REST API

REST는 JSP 화면을 반환하는 것이 아니라 데이터를 반환하는 방식이다.
이번 프로젝트에서는 @RestController를 사용해 JSON 데이터를 응답했다.

@RestController
public class RestControllerTest {

    @RequestMapping(value="/restdto", method=RequestMethod.GET)
    public List<RestDto> getRest() {
        return service.selectList();
    }
}

@Controller는 주로 JSP 화면 이름을 반환하고, @RestController는 반환값 자체를 응답 데이터로 보낸다.

REST에서 자주 쓰는 HTTP 메서드는 다음과 같다.

GET    조회
POST   등록
PUT    수정
DELETE 삭제

등록 요청은 POST로 처리했다.

@RequestMapping(value="/restdto", method=RequestMethod.POST)
public Map<String, Integer> postRest(RestDto dto) {
    int res = service.insert(dto);

    Map<String, Integer> map = new HashMap<String, Integer>();

    if (res > 0) {
        map.put("code", 200);
    } else {
        map.put("code", 500);
    }

    return map;
}

Map<String, Integer>는 key는 문자열, value는 정수인 데이터 구조다.
위 코드는 다음과 같은 JSON 응답으로 변환될 수 있다.

{
  "code": 200
}

9. Spring Boot 시작

StartBoot01에서는 Spring Boot의 기본 실행 구조를 확인했다.

@SpringBootApplication
public class StartBoot01Application {

    public static void main(String[] args) {
        SpringApplication.run(StartBoot01Application.class, args);
    }
}

기존 Spring MVC는 외부 Tomcat에 프로젝트를 올리고 web.xml, servlet-context.xml 같은 설정을 많이 작성했다.
Spring Boot는 내장 Tomcat을 사용하기 때문에 main 메서드로 바로 실행할 수 있다.

JSP를 사용하려면 view 경로 설정이 필요하다.

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

Controller에서 return "test";를 하면 실제로는 다음 JSP를 찾는다.

/WEB-INF/views/test.jsp

10. Spring Boot + JDBC

StartBoot02에서는 Spring Boot에서 JDBC를 사용해 게시판 CRUD를 만들었다.

DB 연결 정보는 application.properties에 작성한다.

server.port=8787

spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XEPDB1
spring.datasource.username=multi
spring.datasource.password=multi

DAO에서는 JdbcTemplate을 사용했다.

@Repository
public class MyDaoImpl implements MyDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<MyDto> selectList() {
        String sql = " SELECT * FROM MYBOARD ORDER BY MYNO DESC ";
        return jdbcTemplate.query(sql, rowMapper);
    }
}

JdbcTemplate은 JDBC 코드를 더 간단하게 작성할 수 있게 해준다.
직접 Connection, PreparedStatement, ResultSet을 매번 열고 닫는 코드를 줄일 수 있다.

Controller에서는 요청을 받아 Biz를 호출하고, 결과를 JSP로 넘긴다.

@GetMapping("/list")
public String selectList(Model model) {
    model.addAttribute("list", biz.selectList());
    return "myboardlist";
}

등록은 POST 요청으로 처리했다.

@PostMapping("/insert")
public String insert(MyDto dto) {
    int res = biz.insert(dto);

    if (res > 0) {
        return "redirect:/myboard/list";
    } else {
        return "redirect:/myboard/insertform";
    }
}

Spring Boot에서도 전체 구조는 기존 MVC와 비슷하다.

Controller
 -> Biz
 -> Dao
 -> JdbcTemplate
 -> Oracle DB

차이점은 설정 방식이다.
기존 Spring MVC는 XML 설정이 많았고, Spring Boot는 application.properties와 자동 설정을 중심으로 동작한다.


마무리

이번 주는 단순 CRUD에서 끝나는 것이 아니라 웹 애플리케이션에 필요한 공통 기능들을 하나씩 붙여보는 시간이었다.

핵심만 정리하면 다음과 같다.

Filter
- Controller 전에 실행되는 공통 처리
- 인코딩, 로그 처리 등에 사용

AOP
- 반복되는 공통 기능 분리
- 로그, 트랜잭션 등에 사용

Interceptor
- Controller 실행 전후에 동작
- 로그인 체크에 적합

Transaction
- 여러 DB 작업을 하나의 단위로 묶음
- 실패 시 rollback

File Upload
- multipart/form-data 필요
- MultipartFile로 파일 처리

REST
- JSP가 아니라 데이터를 응답
- GET, POST, PUT, DELETE 사용

Spring Boot
- 내장 Tomcat으로 실행
- application.properties 중심 설정
- 기존 Spring MVC보다 설정이 간단함

전체적으로 이번 주 학습 내용은 Spring MVC 프로젝트를 실제 웹 애플리케이션에 가깝게 확장하고, 이후 Spring Boot 방식으로 전환하는 흐름이었다.