프로그래밍 일기 — Java의 봄(Spring) — 2일차
Java의 봄은 아직까지는 따뜻하다.
Java의 Spring을 배우는 그 두 번째 날이다. 아직까지는 그리 어렵게 느껴지는 것이 없다. 어제에 이어서 오늘도 몇가지 설정이 더 필요하다. 그럼 Spring을 계속해서 학습해보자.
MySQL
MySQL을 설치해야한다. 다운로드 후 설치하고, 터미널에서 아래와 같은 명령어를 순서대로 입력하여 접속한다.
1. MySQL 경로 들어가기
cd /usr/local/mysql/bin
2. MySQL 접속 (비밀번호 입력 요구)
./mysql -u root -p
팁: Access denied for user ‘root’@’localhost’ (using password: YES)
에러
위 에러는 비번을 틀리거나, 맞게 입력했는데도 접속 권한이 처리가 안된 상태에서 발생할 수 있다. 이 경우 아래의 과정에 따라 문제를 해결 할 수 있다.
/usr/local/
경로의etc/
경로에 들어가my.cnf
라는 파일을 찾는다.
2. 이 파일을 nano my.cnf
를 통해 nano 에디터로 수정한다. [mysqld]
머릿말 밑에 skip-grant-tables
를 입력한다.
skip-grant-tables를 입력한다.
3. ctrl + x
를 눌러 저장하고 나간다. MySQL서버를 재시작한다.
4. 다시 mysql
설치 경로에 들어가 ./mysql -u root -p
입력 후 설치한 MySQL 설정의 비번을 입력하면 로그인된다.
이제 MySQL에 접속이 성공했다. 다음으로 데이터베이스 생성, 확인, 이동(사용) 작업을 수행한다.
1. 데이터베이스 생성
create database academy;
2. 데이터베이스 확인
show databases;
3. 데이터베이스 사용
use academy;
자 이제 데이터베이스의 설정은 끝났다. 이제 기본적인 백엔드 개발환경을 조성한 것이다.
Spring MVC
MVC(Model-View-Controller) 디자인 패턴
아마도 오늘 배울 것 중 가장 중요한 내용이라 할 수 있을 것이다. 소프트웨어 디자인 패턴의 하나로써, 소프트웨어를 구성하는 요소를 기본적으로 Model, View, Controller로 구분해 각각의 역할을 분리하는 방식이다. 소프트웨어의 기본적인 구성요소들을 독립적으로 분리하여 코드의 재사용성 및 유지보수성을 높이며, 개발자간 협업을 용이하게 하기위해 만들어진 개념이라 할 수 있다. 소프트웨어 개발시 이 패턴을 적용해 구조를 잘 설계하는 것이 매우 중요하다.
Model
- 데이터와 비즈니스 로직을 담당
- 데이터베이스와 연동해, 데이터 저장 및 불러오기 등을 수행가능
View
- 사용자 인터페이스 담당
- 사용자에게 보여지는 화면과 버튼, 폼(form)등 디자인 요소 구현
Controller
- Model과 View사이의 상호작용 조정 및 제어
- 사용자의 입력을 받아 Model에 전달하고, Model의 결과를 바탕으로 View에 업데이트
Spring Web MVC
Servlet API 기반으로 구축된 독창적인 웹 프레임워크로써, 처음부터 Spring Framework에 포함되었고, 소스 모듈 이름인 spring-webmvc
에서 유래하였으나, 이제는 일반적으로 Spring MVC로 알려져있다. 중앙에 있는 DispatcherServlet
은 HTTP 요청 처리를 위한 공유 알고리즘을 제공하는 Front Controller 패턴을 중심으로 설계되어있고, 유연하며 다양한 Workflow를 제공한다. 즉, Spring에서는 MVC 디자인 패턴을 적용해 HTTP 요청을 효율적으로 처리하고 있다는 의미다.
Servlet(서블릿)
자바를 이용하여 웹 페이지를 동적으로 생성하는 서버 측 프로그램 혹은 그 사양을 말한다. 그렇다면 사용자가 HTTP요청을 했을 때, 서버의 서블릿이 어떻게 동작할까?
- 사용자는 클라이언트(브라우저)를 통해 서버에 HTTP 요청 즉, API 요청을 한다.
- 요청받은 서블릿 컨테이너(Servlet Container)는
HttpServletRequest
,HttpServletResponse
객체를 생성한다. 약속된 HTTP 규약을 지키면서 쉽게 HTTP에 담긴 데이터를 사용하기 위한 객체이다. - 설정된 정보로 어떠한 서블릿에 대한 요청인지 찾는다. (서블릿의 내부 작업, Servlet 1혹은 2내)
- 해당 서블릿에서 서비스 메서드를 호출한 뒤 브라우저의 요청 메서드에 따라
doGet
혹은doPost
등의 메서드를 호출한다. - 호출한 메서드들의 결과를 그대로 반환하거나 동적 페이지 생성 후
HttpServletResponse
객체에 응답을 담아 클라이언트에 반환 - 응답 완료후 생성한
HttpServletRequest
및HttpServletResponse
객체 소멸
Front Controller
만약 모든 API 요청을 앞의 서블릿 동작 방식에 맞춰 코드를 구현한다면 정말 많은 서블릿 클래스를 구현해야한다(Servlet 1, 2, …등등). 따라서 Spring은 DispatcherServlet
을 사용하여 Front Controller 방식으로 API 요청을 효율적으로 처리한다.
위 Front Controller의 동작 방식은 아래와 같다.
- 클라이언트(브라우저)에서 HTTP 요청을 받으면
DispatcherServlet
객체가 요청을 분석 DispatcherServlet
객체는 분석한 데이터를 토대로 Handler mapping을 통해 Controller를 찾아 요청을 전달 (아래는 이에 대한 예제)
- GET /api/hello → HelloController 의 hello() 함수
- GET /user/login → UserController 의 login() 함수
- GET /user/signup → UserController 의 signup() 함수
- POST /user/signup → UserController 의 registerUser() 함수
Handler mapping에는 API path 및 Controller 메서드가 매칭된다. 예를 들어 아래 코드와 같다.
// Controller 메서드 매칭
@RestController
public class HelloController {
// API path 매칭
@GetMapping("/api/hello")
public String hello() {
return "Hello World!";
}
}
API path 즉, URL을 Controller에 작성하는 방식은 @Controller
에너테이션(annotation)이 달린 클래스를 생성한 후, @GetMapping
처럼 요청한 HTTP 메서드와 일치하는 애너테이션을 추가한 메서드를 구현한다. 해당 메서드의 이름은 URL 매핑과는 관련이 없기 때문에 자유롭게 정해도 좋다. 이 상태에서는 Servlet을 직접 구현하지 않아도 DispatcherServlet에 의해 간편하게 HTTP 요청을 처리할 수 있다.
3. 해당 Controller는 HTTP 요청에 대한 처리를 완료한다.
4. 이후 처리에 대한 결과 즉, 데이터( ‘Model’)와 ‘View’ 정보를 전달한다(Controller -> DispatcherServlet)
5. ViewResolver를 이용해 View에 Model을 적용한다.
6. View를 클라이언트에게 응답으로 전달한다(DispatcherServlet -> Client)
Controller
다시 IntelliJ로 돌아가 실습을 해 본다. Spring Initalizr를 다시 사용하여 새로운 Spring 프로젝트를 만들어 본다. 아래와 같은 설정으로 프로젝트를 생성했다.
- name: spring-mvc
- language: Java
- Build system: Gradle — Groovy
- Group: com.sparta
- JDK: 17
- 프로젝트 경로는 자유
- Dependency에 아래와 같이 Thymeleaf 설정
Controller 장점
예를 들어서 아래와 같은 회원가입 및 로그인 기능을 구현하는 회원 관리 API를 생성하고 싶다고 가정하자.
Servlet 코드는 아래와 같이 구현할 수 있다. 만약 Front Controller 디자인 패턴이 Spring MVC에 적용되지 않았다면, 아래와 같이 오버라이딩을 통해 하나의 클래스에서 상속한 메서드를 필요에 맞게 고치는 방법으로 구현하기 힘들었을 것이다. 오히려 이 모든 API를 처리하기 위해 개별적인 3개의 클래스를 만들어야했을 것이기 때문이다.
// 로그인 기능
@WebServlet(urlPatterns = "/user/login")
public class UserLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// ...
}
}
// 로그아웃 기능
@WebServlet(urlPatterns = "/user/logout")
public class UserLogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// ...
}
}
// 회원 가입 기능
@WebServlet(urlPatterns = "/user/signup")
public class UserSingUpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// ...
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
// ...
}
}
API마다 파일을 만들 필요가 없어졌기 때문에 유사한 성격의 API를 하나의 Controller로 관리할 수 있다. 그렇다고해서 하나의 Controller에 모든 API를 넣는 것은 아니다. API의 사용 용도가 비슷한 것끼리 Controller를 이용해 그룹화한다고 보면된다. 메서드 이름역시 자유롭게 설정이 가능하나, 클래스 내에서 중복되는 이름을 가질 수는 없기 때문에 이에 유의한다.
// 회원 관리 API 메서드를 담고있는 UserController 클래스
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
public String login() {
// ...
}
@GetMapping("/logout")
public String logout() {
// ...
}
@GetMapping("/signup")
public String signup() {
// ...
}
@PostMapping("/signup")
public String registerUser(SignupRequestDto requestDto) {
// ...
}
}
또한 @Controller
Annotation을 쓰면, 해당 클래스가 Controller의 역할을 수행할 수 있도록 등록할 수 있다. 이후 GET/POST/PUT/DELETE 메서드를 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping
Annotation으로 쉽게 작성할 수 있다.
// Controller 클래스
@Controller
public class HelloController {
@GetMapping("/api/hello")
@ResponseBody
public String hello() {
return "Hello World!";
}
}
// GET Annotation
@GetMapping("/api/get")
@ResponseBody
public String get() {
return "GET Method 요청";
}
// POST Annotation
@PostMapping("/api/post")
@ResponseBody
public String post() {
return "POST Method 요청";
}
// PUT Annotation
@PutMapping("/api/put")
@ResponseBody
public String put() {
return "PUT Method 요청";
}
// DELETE Annotation
@DeleteMapping("/api/delete")
@ResponseBody
public String delete() {
return "DELETE Method 요청";
}
위 Annotation을 종합하여 모든 HTTP 메서드를 구현한 코드를 아래 예제에서 확인할 수 있다.
package com.sparta.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/")
public class HelloController {
@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello World";
}
@GetMapping("/get")
@ResponseBody
public String get() {
return "GET Method 요청";
}
@PostMapping("/post")
@ResponseBody
public String post() {
return "POST Method 요청";
}
@PutMapping("/put")
@ResponseBody
public String put() {
return "PUT Method 요청";
}
@DeleteMapping("/delete")
@ResponseBody
public String delete() {
return "DELETE Method 요청";
}
}
정적 페이지와 동적 페이지
이번에는 정적(static) 페이지와 동적(dynamic) 페이지의 차이점을 알아보자. 정적 페이지란 이미 완성되어 더 이상의 수정이 필요없는 페이지를 의미하고, 동적 페이지란 아직 업데이트가 계속 필요한 페이지를 말한다. 아래와 같이 hello.html
이라는 파일을 /resources/static/
경로 내 생성한다.
<!-- Hello.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello Spring</title>
Spring Master - 1주차 84
</head>
<body>
Hello, Spring 정적 웹 페이지!! (static)
</body>
</html>
이렇게하면 SpringBoot 서버에 html 파일을 바로 요청할 경우, 해당 파일을 static 폴더에서 찾아 반환한다. Controller를 거쳐서 html을 반환하는 방법도 있지만, 이미 완성된 정적인 페이지의 경우 굳이 Controller를 통해 반환할 필요가없다.
이러한 정적 페이지를 사용하는 방법은 아래와 같다. GET 메서드이며, "{폴더 이름}-{html 파일이름}"
를 매개변수로 받고, "{html 파일 이름}.html"
을 리턴한다.
// 동적 페이지 활용법
@GetMapping("/static-hello")
public String hello() {
return "hello.html";
}
만약 Controller를 통해 반환하는 것을 테스트해보고 싶다면, dependencies에서implementation ‘org.springframework.boot:spring-boot-starter-thymeleaf’
를 주석처리해야한다. thymeleaf
는 동적 페이지 처리를 위한 템플릿 엔진으로써, Controller가 파일을 찾는 경로를 resources/templates
로 설정한다.
아래와 같이 템플릿 엔진을 적용한 상태에서 static
폴더의 html 파일을 Controller를 통해 처리하고 싶다면 “redirect:/hello.html”
redirect 요청을 문자열로 반환해 http://localhost:8080/hello.html 요청이 재 수행되면서 static
폴더의 파일을 반환한다.
Template엔진에 View 전달
static
폴더에 있는 html 파일을 바로 호출하는 것이 가장 간단하나, 브라우저에서 바로 접근하지 못하게하고 싶다면, 혹은 특정 상황에 Controller를 통해 제어를 원할 경우, 이렇게 templates
폴더에 해당 정적 html 파일을 추가하고 해당 html 파일의 이름인 "hello"
문자열을 반환하는 방식으로 처리가능하다. 이때 .html
확장자는 생략된다.
// redirect 방법
@GetMapping("/html/redirect")
public String htmlStatic() {
return "redirect:/hello.html";
}
// 전체코드
package com.sparta.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/")
public class HelloController {
// @GetMapping("/static-hello")
// public String hello() {
// return "hello.html";
// }
@GetMapping("/html/redirect")
public String htmlStatic() {
return "redirect:/hello.html";
}
@GetMapping("/html/templates")
public String htmlTemplates() {
return "hello";
}
@GetMapping("/get")
@ResponseBody
public String get() {
return "GET Method 요청";
}
@PostMapping("/post")
@ResponseBody
public String post() {
return "POST Method 요청";
}
@PutMapping("/put")
@ResponseBody
public String put() {
return "PUT Method 요청";
}
@DeleteMapping("/delete")
@ResponseBody
public String delete() {
return "DELETE Method 요청";
}
}
동적페이지 처리하기
동적페이지 역시 templates
폴더를 사용할 수 있다. 아래와 같이 hello-visit.html
파일을 이 폴더에 생성하고, 동적페이지 처리를 위한 메서드를 Spring 어플리케이션 파일에 정의한다.
동적 페이지의 처리 과정을 요약한다면 다음과 같다.
- Client의 요청을 Controller에서 Model로 처리한다. DB조회가 필요할 시, DB작업 후 처리한 데이터를 Model에 저장한다.
- Template engine(Thymeleaf)에게 View, Model을 전달한다. View는 동적 HTML 파일이며, Model은 View에 적용할 정보들이 된다.
- View에 Model을 적용하여 동적 웹페이지를 생성한다. 예를 들어 로그인이 성공한다면, 로그인된 사용자의 Nickname을 페이지에 추가할 수 있다. Template engine 종류에는 타임리프(Thymeleaf), Groovy, FreeMaker, Jade, JSP등이 있다.
- 다음으로 클라이언트(브라우저)에게 View(동적 웹 페이지, HTML)을 전달한다.
<!-- hello-visit.html -->
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Hello Spring</title></head>
<body>
<div>
Hello, Spring 동적 웹 페이지!!
</div>
<div>
(방문자 수: <span th:text="${visits}"></span>)
</div>
</body>
</html>
// 동적페이지 처리 메서드
// UI Model 패키지 추가
import org.springframework.ui.Model;
// 클래스 내 아래 변수 추가
private static long visitCount = 0;
// 동적 페이지 처리 메서드
@GetMapping("/html/dynamic")
public String htmlDynamic(Model model) {
visitCount++;
model.addAttribute("visits", visitCount);
return "hello-visit";
}
// 동적 페이지 예제
package com.sparta.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/")
public class HelloController {
private static long visitCount = 0;
// @GetMapping("/static-hello")
// public String hello() {
// return "hello.html";
// }
@GetMapping("/html/redirect")
public String htmlStatic() {
return "redirect:/hello.html";
}
@GetMapping("/html/templates")
public String htmlTemplates() {
return "hello";
}
@GetMapping("/html/dynamic")
public String htmlDynamic(Model model) {
visitCount++;
model.addAttribute("visits", visitCount);
return "hello-visit";
}
@GetMapping("/get")
@ResponseBody
public String get() {
return "GET Method 요청";
}
@PostMapping("/post")
@ResponseBody
public String post() {
return "POST Method 요청";
}
@PutMapping("/put")
@ResponseBody
public String put() {
return "PUT Method 요청";
}
@DeleteMapping("/delete")
@ResponseBody
public String delete() {
return "DELETE Method 요청";
}
}
Thymeleaf
View 정보
위 HTML에서 View에 해당하는 정보는 아래와 같다.
"hello-visit"
->resources/templates/hello-visit.html
<div>
(방문자 수: <span th:text="${visits}"></span>)
</div>
Model 정보
- 위 HTML의
visits
는 방문 횟수를 말하며 관련 변수가 자바 파일에서는visitCount
로 선언되어있다. 예를 들어 방문 횟수가 1백만 번 이라 하면 아래와 같이 표기가 되야할 것이다.
<div>
(방문자 수: <span>1000000</span>)
</div>
위 정적 및 동적 페이지를 처리할 수 있는 메서드를 활용한 Spring 어플리케이션을 작성해보자. 아래와 같은 예가 될 수 있다. 아래코드에서는 방문자 수를 나타내는 visitCount
변수의 값이 html/dynamic
을 호출할 때마다 증가하도록 짜여져있다. Model에서 이를 처리하여 View에 전달하고 View는 이 정보를 visits
라는 이름으로 처리하여 브라우저에 전달한다.
package com.sparta.springmvc.html;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HtmlController {
private static long visitCount = 0;
@GetMapping("/static-hello")
public String hello() {
return "hello.html";
}
@GetMapping("/html/redirect")
public String htmlStatic() {
return "redirect:/hello.html";
}
@GetMapping("/html/templates")
public String htmlTemplates() {
return "hello";
}
@GetMapping("/html/dynamic")
public String htmlDynamic(Model model) {
visitCount++;
model.addAttribute("visits", visitCount);
return "hello-visit";
}
}
데이터를 브라우저에 반환하는 방법
최근 개발업계에서는 Response즉 서버가 클라이언트에 하는 응답을 처리하는데 있어 변화가 있다. 일반적으로 우리는 서버가 클라이언트(브라우저)의 요청을 받으면 html/css/js
파일 등을 반환해 그것을 클라이언트가 그려주는 것으로 이해하고 있다.
하지만 최근 트렌드는 이 것이 꼭 정답은 아니라는 것이다. 점차 프론트엔드와 백엔드가 별도의 영역으로 발전하게 되면서, 둘을 느슨하게 결합하는 방식을 더 많이 채택하게 되었기 때문이다. 요즘은 서버가 직접 View(html/css/js)를 반환하기 보다는 요청에 맞는 특정한 정보만 반환하는 것을 조금 더 선호하기도 한다. 따라서 서버에서는 주로 데이터 교환 포맷 중 JSON 형태로 데이터를 반환하기도한다.
이에 따라서 기본적으로 요청해야하는 뼈대인 HTML 파일 대신, JSON 데이터를 요청하는 API를 통해서 브라우저에 HTML을 통해 JSON 데이터를 반영해 그려주는 연습이 필요할 수 있다. 다행인 것은 JS및 HTML은 이미 프론트엔드의 영역이므로, 백엔드 엔지니어는 서버를 만드는데만 집중하면된다는 것이다.
오늘의 생각/느낌/다짐
오늘은 본격적으로 Spring의 주요 개념인 MVC(Model, View, Control)에 대해 배워보았다. 이 내용이 Spring의 설계에 있어 매우 중요한 디자인 패턴이라는 개념인 만큼, 이를 이해하고 넘어가는 것이 앞으로의 여정에 있어 매우 중요할 것이라는 직감이 들었다.
내가 이해한 MVC는 영화 스크린과 같다. Controller라는 제어기가 영화의 콘텐츠가 어떻게 처리될 것인지를 정의하고 그 방식대로 작업을 수행한다. 이후 Model에 처리된 영화의 데이터들을 보내고 Model은 View에게 그것을 어떻게 보여줄 것인지 알려준다. View는 그것을 정해진 방식대로 처리하여 프로젝트의 빔을 통해 관객에게 처리된 영화의 씬들을 보여준다. 가장 뒤에 있는 주방장이 Controller, 그 사이에 있는 레스토랑 매니저가 Model 그리고 서빙홀 매니저인 View가 최전방에서 유저와 상호작용하는 것이라는 생각이들었다.
이 개념이 Spring의 꽃이라고해서 처음에 많이 겁을 먹었던 것이 사실이지만, 생각보다 그렇게 어려운 개념은 아니어서 다행이었다. 또한 클래스와 추상화에 대한 이해를 아예 하지 않은 것이 아니라 그래도 최대한 이해하려고 노력하고 들어온 덕분인지, Spring이 사용하는 많은 주석(Annotation)도 그렇게 낮설게 느껴지지는 않았다. 이해가 되지 않아 클래스 코드를 짜는 방식에 대해 일부 외운 것도 있었지만, 오히려 그것이 내게 도움이 되었다.
오늘 아침에 마침 시니어 개발자 코치님으로부터 조언을 들을 수 있었다. 내가 이렇게 외워서 공부를 하는 것이 맞는지에 대한 의문이 있었기 때문이다. 하지만 그는 외우는 건 어쩌면 자연스러운 배움의 방식이고, 프로그래밍 세계에서도 아예 외우지 않고 학습을 하는 것은 불가능하다고 말씀하셨다. 컴퓨터의 언어도 어쨌든 하나의 언어라서 컴퓨터가 하는 말의 단어를 이해해야하는데, 그것을 처음에는 어느 정도 외우지 않고 이해하기는 어렵기 때문이다. 왜 프로그래밍에서 for문이 반복문을 의미하는지, if문이 왜 조건문인지를 언어학적인 접근으로 다가갈수도 있겠지만, 그렇게해서 항상 인간의 언어와 동일해질 수는 없다. 따라서 컴퓨터의 용어와 문법도 어느 정도 반복적으로 익혀야하는 것일 수 밖에 없다.
알고리즘을 공부할때도 마찬가지다. 알고리즘 역시 반복적으로 풀다보면 그 방법론 들이 어느샌가 손과 머리에 익는 것이지, 그것을 개념적으로 수학적으로 처음부터 다가가면서 원리를 파악해 풀려고한다면 너무 많은 시간이 걸릴 수 있다. 그럴 때는 일단 다른 사람이 푼 것을 참조하여, 그 사람이 어떤 로직이나 논리로 문제에 접근했는지를 파악하고, 그것을 바탕으로 나만의 코드를 써보는 방식으로 시작하는 것이 좋다. 또한 그렇게 했을 때에도 도저히 풀 수 없는 것이 있다면 그때는 다른 사람의 코드를 배껴서라도 그 부분을 해결하고 넘어가야한다. 너무 오래 매달리면 비효율적일 것이기 때문이다. 그렇게 내가 해결하지 못한 부분을 참조하여 그 부분을 반복적으로 기억하려고 노력한다면, 비슷한 문제가 왔을 때 그 방법이 생각나서 그것을 활용해 문제를 풀어 나갈 수 있다.
결국 우리가 처음 부터 기본적으로 배울 때 외우는 것에서 완벽하게 자유로울 수는 없다. 처음부터 세상의 이치를 이해했다면 나는 고다마 싯다르타 이후 세계적인 현자가 되었을 것이다. 하지만, 우리는 모두다 엄청난 천재가 아니다. 그렇다면 시작할 수 있는 시점이 필요한데, 그러기 위해서는 무엇인가 반복적으로 숙달할 필요가 있다. 우리가 태어났을 때 부터 “사과"를 사과로 이해하는 것이 아니라, 어느 정도 학습을 할 수 있는 연령대가 되었을 때 어떠한 개념에 대해 약속된 언어를 우리가 자연스럽게 그냥 접하면서 알게되는 것처럼, 그 것이 그냥 약속된 규칙이므로 그냥 그렇게 받아들인 것이다. 그것이 왜 “사과"인지를 한자나 언어사적인 맥락에서 공부하는게 아니라는 것이다.
그 말을 들으니 어느 정도 외워서 공부하는 것이 그렇게 나쁜 것만은 아니라는 생각이들었다. 어차피 모든 배움은 반복 숙달이고, 반복 숙달을 하는 목적은 몸과 머리가 기억하게 만들기 위해서다. 근본적으로 따져보면 몸과 머리가 외우게하기 위해서라는 것이다. 내가 이렇게 반복적으로 알고리즘을 숙달한 것이 결국 Java를 어떻게 사용해야하는지에 대해서 어느 정도 몸과 머리가 기억하게 만들었기에 지금의 Spring을 또 더 잘 배울 수 있는 준비가 된거라 생각한다. 그러니 이 자신감을 가지고 계속 배움에 정진하다보면, 어느새 나도 잠재력이 있는 신입으로 가르칠만한 개발자가 되어있지 않을까? 그런 희망을 품으면서 또 다음 주 한 주를 기대하게된다.
참조:
(1) https://pixabay.com/photos/dandelion-heaven-flower-nature-463928/
(3) https://www.geeksforgeeks.org/servlet-architecture/
(4) https://medium.com/@wminikuma/spring-spring-5-mvc-with-java-based-configuration-fdf3cba27dae