Today's Codekata
// 최대 공약수와 최소 공배수
class Solution {
public int[] solution(int n, int m) {
int[] answer = new int[2];
for (int i = 1; i <= Math.min(n, m); i++) {
if (n % i == 0 && m % i == 0) {
answer[0] = i;
}
}
for (int i = 1; i <= n * m; i++) {
if (i % n == 0 && i % m == 0) {
answer[1] = i;
break;
}
}
return answer;
}
}
최소 공배수 = (n * m) / 최대 공약수 같은 공식도 있음을 알게 되었다.
// 3진법 뒤집기
class Solution {
public int solution(int n) {
String num = "";
while (0 < n) {
num += Integer.toString(n % 3);
n /= 3;
}
return Integer.parseInt(num, 3);
}
}
3진법으로 변환한 뒤에 뒤집는 과정을 만들어야했는데 그러지 못 했다.
num = new StringBuilder(num).reverse().toString(); StringBuilder를 활용하는 방식이 가장 많이 보였다.
-- 오랜 기간 보호한 동물
SELECT AI.NAME, AI.DATETIME
FROM ANIMAL_INS AI
LEFT JOIN ANIMAL_OUTS AO ON AI.ANIMAL_ID = AO.ANIMAL_ID
WHERE AO.ANIMAL_ID IS NULL
ORDER BY AI.DATETIME
LIMIT 3
-- 카테고리 별 도서 판매량 집계하기
SELECT B.CATEGORY,
SUM(BS.SALES) AS TOTAL_SALES
FROM BOOK B
LEFT JOIN BOOK_SALES BS ON B.BOOK_ID = BS.BOOK_ID
WHERE BS.SALES_DATE BETWEEN '2022-01-01' AND '2022-01-31'
GROUP BY 1
ORDER BY 1
-- 상품 별 오프라인 매출 구하기
SELECT P.PRODUCT_CODE,
SUM(P.PRICE * OS.SALES_AMOUNT) AS SALES
FROM PRODUCT P
JOIN OFFLINE_SALE OS ON P.PRODUCT_ID = OS.PRODUCT_ID
GROUP BY 1
ORDER BY 2 DESC, 1
JOIN 종류
| 종류 | 설명 |
| INNER JOIN(=JOIN) | 두 테이블 모두에서 조건에 맞는 행만 반환 |
| LEFT JOIN | 왼쪽 테이블의 모든 행 + 일치하는 오른쪽 행, 없으면 NULL |
| RIGHT JOIN | 오른쪽 테이블의 모든 행 + 일치하는 왼쪽 행, 없으면 NULL |
| FULL OUTER JOIN | 양쪽 테이블의 모든 행 포함, 일치하지 않으면 NULL |
| CROSS JOIN | 두 테이블의 모든 행 조합 (카티션 곱) |
| SELF JOIN | 자기 자신과 JOIN, 계층 구조나 비교가 필요할 때 사용 |
Today I Learned
Spring MVC
MVC 패턴 + 프론트 컨트롤러 패턴 + 어댑터 패턴
동작 순서
1. 클라이언트 요청 수신 (DispatcherServlet)
2. URL → Handler 매핑 (HandlerMapping)
3. Handler 실행을 위한 Adapter 탐색 (HandlerAdapter)
4. Handler 실행 → 결과 반환 (ModelAndView)
5. ViewResolver 호출 → View 객체 결정
6. View 렌더링 (HTML or JSON 등으로 응답 생성)
내부 작동 원리 (코드 흐름)
protected void doDispatch() {
mappedHandler = getHandler(request); // 핸들러 탐색
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 어댑터 탐색
mv = ha.handle(request, response, mappedHandler.getHandler()); // 핸들러 실행
render(mv, request, response); // 결과 렌더링
}
Spring 사용 시 자주 사용되는 Annotation
| 어노테이션 | 설명 |
| @Component | 일반 클래스를 Bean으로 등록하기 위한 어노테이션 |
| @Service | 비즈니스 로직을 담당하는 클래스에 붙이는 어노테이션 |
| @Repository | DB 접근을 담당하는 클래스에 사용하는 어노테이션 |
| @RestController | REST API용 컨트롤러를 정의할 때 사용하는 어노테이션 |
| @Autowired | 필요한 Bean을 자동으로 주입하는 어노테이션 |
| @Value | 설정 파일(application.properties)의 값을 주입할 때 사용 |
| @GetMapping, @PostMapping | HTTP 요청(GET/POST 등)을 처리하는 메서드에 사용 |
| @RequestParam | URL 쿼리 파라미터를 메서드 인자로 받을 때 사용 |
| @PathVariable | URL 경로의 변수를 메서드 인자로 받을 때 사용 |
| @RequestBody | 요청의 JSON 데이터를 객체로 변환해 받을 때 사용 |
| @Transactional | 트랜잭션 처리를 위한 어노테이션 (오류 시 롤백 등) |
| @SpringBootApplication | Spring Boot 프로젝트의 진입점에 사용하는 어노테이션 |
Request Mapping 조건 매핑
| 속성 | 설명 | 예시 |
| params | 요청 파라미터 조건에 따라 매핑 | params = "gender=man" → ?gender=man일 때만 실행 |
| headers | 특정 HTTP 헤더 조건에 따라 매핑 | headers = "Content-Type=application/json" → 해당 헤더가 있어야 실행 |
| consumes | 요청의 Content-Type 조건 설정 | consumes = "application/json" → JSON 요청만 허용 |
| produces | 응답의 Content-Type 조건 설정 | produces = "text/plain" → 응답을 text/plain으로 제공 |
JPA vs JDBC 비교
| 항목 | JDBC | JPA |
| 정의 | Java Database Connectivity | Java Persistence API |
| 방식 | SQL 직접 작성 | ORM 기반 (객체 ↔ 테이블 자동 매핑) |
| 코드량 | 많고 반복적임 | 간결하고 추상화됨 |
| 유지보수 | SQL 수정 시 코드도 직접 수정 | Entity중심으로 관리되어 유지보수 용이 |
| 성능 제어 | SQL 직접 제어 가능 | 내부에서 SQL 자동 생성 (튜닝 어려움도 있음) |
| 사용 예 | PreparedStatement, ResultSet 등 | @Entity, @Repository, @Id 어노테이션 기반 |
HTTP 요청 데이터
| 방식 | 특징 |
| GET + Query Parameter | 간단한 조회, URL에 데이터 포함 |
| POST + HTML Form | Form 데이터 전송, getParameter() 사용 가능 |
| Request Body (JSON) | 구조화된 데이터, 객체 변환 필요, API 통신에 적합 |
마치며
Spring은 단순히 잘 만들어진 프레임워크를 넘어, 개발자의 역량을 더 쉽고 강하게 발휘할 수 있도록 돕는 도구라는 걸 느꼈다.
이전엔 그 동작 방식이 마치 마법처럼 이해할 수 없는 무언가같이 느껴졌는데, 이제는 내부 흐름을 하나하나 배워가며 이해의 폭이 넓어지고 있다. 앞으로는 이론과 실습을 병행하면서, 지금까지 배운 내용을 몸에 익히는 시간을 가져보려 한다.