
위의 톰캣 소스 코드가 "/posts"라는 글자를 자바 객체 바구니에 담아주었기 때문에, 비로소 우리가 작성한 소스 코드의 매개변수로 전달되어 들어옵니다.
이 부분에서 이해력 저하.
RequestMapping("/posts") //URL prefix: 이 컨트롤러가 처리하는 URL 앞에 공통적으로 붙입니다.
이 부분으로 넘겨지는 순간의 프로세스를 인지 못했음, 이걸 이해해야 도서관 앱 만들 수 있을것으로 예상.
도서관앱은 잘되더라도 이해 못할것으로 예상되지만 알고리즘 파악 연습하겠음.
[ 웹 브라우저 (클라이언트) ]
│ ▲
(1) 요청 │ │ (8) 최종 HTML 응답
▼ │
┌──────────────────────────┴──────────────────────────┐
│ [ Web / Security Layer ] │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ SecurityConfig (시큐리티 성벽) │ │
│ │ - /member/login, /join 🔓 (PermitAll) │ │
│ │ - /posts, /posts/write 🔒 (Authenticated) │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ (2) 검문 통과 │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Controller (요청 길목) │ │
│ │ [MemberController][PostController] │ │
│ └──────────┬──────────────────────┬───────────┘ │
│ │ │ │
│ │ (3) 유효성 검증 │ (7) Model │
│ │ (@Size, @NotBlank)│ 데이터 주입│
│ ▼ ▼ │
│ ┌────────────────────┐ ┌─────────────────────┐ │
│ │ Request DTO 바구니 │ │ Thymeleaf 템플릿 │ │
│ │ - SignUpRequest │ │ - list.html / write │ │
│ │ - PostWriteRequest │ │ - th:if / th:each │ │
│ └──────────┬─────────┘ └─────────────────────┘ │
└──────────────┼──────────────────────────────────────┘
│ (4) DTO -> Entity 변환
▼
┌─────────────────────────────────────────────────────┐
│ [ Persistence & DB Layer ] │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ MyBatis XML Mapper (SQL 징검다리) │ │
│ │ - MemberMapper.xml (save / findByUsername) │ │
│ │ - PostMapper.xml (findAll / resultMap) │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ (5) SQL 쿼리 전송 │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ DataBase (데이터 창고) │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ member 테이블│ ◄─────N:1│ post 테이블 │ │ │
│ │ └──────────────┘ (외래키)└──────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ (6) 데이터 반환 (ResultMap) │
└─────────────────────────────────────────────────────┘
[ 컨트롤러 실행 중 에러 발생!! ]
│
│ (⚡ 예외 전파 / Throw)
▼
┌──────────────────────────────────────────┐
│ GlobalExceptionHandler (전역 구급상자) │
│ │
│ - @ExceptionHandler(IllegalArgument) │ ───► [ 📝 SLF4J 에러 로그 기록 ]
│ - @ResponseStatus(400 / 500) │
└────────────────────┬─────────────────────┘
│
│ (Model에 status, message 주입)
▼
┌──────────────────────────────────────────┐
│ error.html (공통 에러 안내 화면) │
│ │
│ - th:if="{status == 500}" 🔥 빨강 타이틀│
└────────────────────┬─────────────────────┘
│
▼
[ 🏠 게시판 목록으로 돌아가기 버튼 ]
[ 유저가 브라우저에 http://localhost:8080/posts 입력 ]
│
▼
SecurityConfig.java의 인가 명령어 실행
└─ .anyRequest().authenticated()
└─ 문지기 필터가 내부적으로인증 세션 확인 후 다음 명령 호출 ──► filterChain.doFilter(req, res);
│
┌──────────────────────────────────────────────────────────────────────┘
▼
PostController.java의 매핑 명령어가 동작하여 메서드 실행
└─ @GetMapping("/posts")
└─ public String list(Model model) { ... }
│
├─► [소스 내 명령] postMapper.findAll() 호출!
│ │
┌──────────┴──────────────────────┘
▼
PostMapper 인터페이스 & PostMapper.xml의 SQL 매핑 명령 실행
└─
└─ DB에서 조인 쿼리 실행 후 결과를 객체로 조립하는 명령 작동
│
├─► [소스 내 명령]
├─► [소스 내 명령]
│ │
┌─────────────┴──────────────────────┘
▼
다시 PostController.java로 데이터가 돌아와 화면(View)으로 넘기는 명령 실행
└─ model.addAttribute("posts", posts); // 바구니에 담기
└─ return "post/list"; // 타임리프로 제어권 토스!
│
┌────────────────────────────────────────────┘
▼
list.html (Thymeleaf) 화면 가공 명령 실행
└─ th:each="post : {posts}" // 반복해서 줄 만들기 명령
└─ th:text="{post.title}" // 글 제목 출력 명령
└─ th:if="${#authorization.expression('isAuthenticated()')}" // 로그인 유저 확인 명령
[SignUpRequest.java]
└─ @Size(min = 4, max = 20) private String username;
└─ 브라우저가 보낸 값이 DTO 규격에 맞는지 검사 명령 발동!
[MemberController.java]
└─ @PostMapping("/member/join")
└─ public String join(@Valid @ModelAttribute("memberForm") SignUpRequest dto, ...) {
│
├─► [소스 내 명령] if (bindingResult.hasErrors()) -> 실패 시 화면 백(Back)
├─► [소스 내 명령] if (!dto.getPassword().equals(dto.getPasswordConfirm()))
│ └─ bindingResult.rejectValue("passwordConfirm", ...);
│
├─► [소스 내 명령] Member member = new Member(dto.getUsername(), ...); (객체 생성)
├─► [소스 내 명령] memberMapper.save(member); 호출!
│ │
┌───────┴──────────────────────┘
▼
[MemberMapper.xml]
└─
└─ DB에 INSERT 실행 후, 방금 생성된 PK 번호를 자바 member 객체에 꽂아주는 명령(keyProperty) 완성!
[MemberController.java]
└─ return "redirect:/member/login"; // 로그인 페이지로 강제 이동 명령 실행 🎉
C:\Users\tj-bu-707-12\Documents\이재웅\boardapp_debug\boardapp\src\main\java\com\controller
C:\Users\tj-bu-707-12\Documents\이재웅\boardapp_debug\src\main\java\com\example\boardapp\controller
💥 소스 코드 명령어 기준 인과관계 체인 (Source Code Causality)
1단계: 보안 성벽 패스 (SecurityConfig.java)
원인 (Cause): 브라우저가 GET /posts 주소로 서버에 노크함.
인과 작용: SecurityConfig 클래스의
.authorizeHttpRequests(...) 성벽이 발동하여 유저의 로그인 세션을 검사함.
결과 (Effect): 인증이 확인되어 시큐리티 내부의 filterChain.doFilter(request, response); 명령이 실행됨. 이 명령이 실행되었기 때문에 다음 단계인 PostController로 진입할 수 있는 문이 열림.
2단계: 컨트롤러 라우팅 및 데이터 요청 (PostController.java)
원인 (Cause): 시큐리티가 문을 열어주자 스프링의 DispatcherServlet이 주소를 분석함.
인과 작용: PostController 클래스 위에 적힌 @GetMapping("/posts") 어노테이션이 주소를 낚아채서
public String list(Model model) 메서드를 깨움(호출함).
결과 (Effect): 메서드 내부 첫 줄에 적힌 postMapper.findAll() 명령어가 실행됨. 이 자바 메서드가 호출되었기 때문에 데이터베이스를 조회하는 다음 도미노가 쓰러짐.
3단계: SQL 실행 및 객체 조립 (PostMapper.xml)
원인 (Cause): 컨트롤러가 postMapper.findAll() 메서드를 호출함.
인과 작용: MyBatis 엔진이 PostMapper.xml 파일로 찾아가 인터페이스명과 매칭되는 태그를 찾아 내부의 SELECT p.id as post_id ... FROM post p INNER JOIN member m ... SQL 문을 DB로 발사함.
결과 (Effect): DB가 가공되지 않은 평평한 테이블 데이터를 반환하면, XML 상단에 선언해 둔 과 <association property="member" ...> 명령이 차례로 작동하여 순수 데이터를 자바의 Post 객체와 Member 객체 구조로 예쁘게 조립해 냄.
4단계: 가공 데이터 전달 (PostController.java 리턴)
원인 (Cause): MyBatis가 조립을 완료하여 최신순으로 정렬된 List 객체 뭉치를 컨트롤러에게 돌려줌.
인과 작용: PostController가 바동을 이어받아 다음 두 줄의 소스 코드를 실행함.
model.addAttribute("posts", posts); (화면 배달용 바구니에 데이터를 담는 명령)
return "post/list"; (타임리프야 화면 그려라! 제어권 토스 명령)
결과 (Effect): 컨트롤러 메서드가 안전하게 종료되면서, 뷰 템플릿 엔진이 깨어나는 원인이 됨.
5단계: 동적 화면 출력 (list.html)
원인 (Cause): 컨트롤러가 "post/list"라는 문자열과 데이터 바구니(model)를 던짐.
인과 작용: 타임리프 엔진이 list.html 소스 코드를 위에서 아래로 읽으며 해석함.
명령이 전달받은 게시글 개수만큼 HTML 행을 복사해 냄.명령이 각 게시글의 ID를 추출해 주소창 파라미터(?id=1)를 동적으로 엮고 제목 텍스트를 화면에 찍어줌.
결과 (Effect): 최종적으로 결합한 순수 HTML 파일이 유저의 브라우저 화면에 렌더링 됨.
[.authorizeHttpRequests()] (시큐리티 허가)
│
▼ [원인이 되어 결과를 낳음]
[@GetMapping("/posts")] (컨트롤러 매핑 매칭)
│
▼ [원인이 되어 결과를 낳음]
[postMapper.findAll()] (자바 메서드 호출)
│
▼ [원인이 되어 결과를 낳음]
[] (MyBatis XML 내 SQL 매핑 및 실행)
│
▼ [원인이 되어 결과를 낳음]
[ / ] (DB 데이터를 자바 객체 계층으로 조립)
│
▼ [원인이 되어 결과를 낳음]
[model.addAttribute() / return "post/list"] (데이터 적재 및 뷰 지정)
│
▼ [최종 결과]
[th:each / th:text] (타임리프가 동적 HTML을 완성하여 브라우저에 출력)