Today's Codekata
// 이상한 문자 만들기
public class Solution {
public String solution(String s) {
String answer = "";
int idx = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
answer += ' ';
idx = 0;
} else {
if (idx % 2 == 0) {
answer += Character.toUpperCase(s.charAt(i));
} else {
answer += Character.toLowerCase(s.charAt(i));
}
idx++;
}
}
return answer;
}
}
// 삼총사
public class Solution {
public int solution(int[] number) {
int answer = 0;
for (int i = 0; i < number.length; i++) {
for (int j = i + 1; j < number.length; j++) {
for (int k = j + 1; k < number.length; k++) {
if (number[i] + number[j] + number[k] == 0) {
answer++;
}
}
}
}
return answer;
}
}
i = 0, j = 1, k = 2 이런 식으로 줬더니 중복 인덱스를 조회하는 경우가 생겨서 인덱스의 시작점이 겹치지 않게 만들어줬다!
// 크기가 작은 부분 문자열
public class Solution {
public int solution(String t, String p) {
int answer = 0;
String num = "";
for (int i = 0; i <= (t.length() - p.length()); i++) {
for (int j = i; j < (p.length() + i); j++) {
num += t.charAt(j);
}
if (num.compareTo(p) <= 0) {
answer++;
}
num = "";
}
return answer;
}
}
for문을 두 번 돌리는 것보다 t.substring(i, i + p.length())로 문자열의 일부를 추출하는 방법이 더 좋아보인다.
-- 있었는데요 없었습니다
SELECT I.ANIMAL_ID, I.NAME
FROM ANIMAL_INS I
INNER JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
WHERE I.DATETIME > O.DATETIME
ORDER BY I.DATETIME
-- 오랜 기간 보호한 동물
SELECT I.ANIMAL_ID, I.NAME
FROM ANIMAL_INS I
INNER JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
ORDER BY O.DATETIME - I.DATETIME DESC
LIMIT 2
-- 보호소에서 중성화한 동물
SELECT I.ANIMAL_ID, I.ANIMAL_TYPE, I.NAME
FROM ANIMAL_INS I
INNER JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
WHERE I.SEX_UPON_INTAKE LIKE 'Intact%'
AND (O.SEX_UPON_OUTCOME = 'Spayed Female'
OR O.SEX_UPON_OUTCOME = 'Neutered Male')
ORDER BY I.ANIMAL_ID
IN 연산자를 활용하면 IN (Spayed Female, Neutered Male) 이런 식으로 여러 값 중 하나와 일치하는지 확인할 수 있다.
Today I Learned
Spring 요청 데이터
1.@RequestParam — 주소창의 파라미터 받기
- 주로 GET 방식에서 사용되며 URL의 ?key=value 형식 데이터를 매핑한다.
- 어노테이션의 생략이 가능하지만 협업에 혼동을 주므로 최소한 @RequestParam은 명시하는 것을 권장한다.
- required = true (기본값): 파라미터 없으면 400 에러 발생
required = false: 파라미터 없어도 되지만 int는 null이 안 됨으로 Integer 사용
- defaultValue = "값" 지정 시 파라미터가 없거나 빈 문자열일 때 자동으로 채워진다.
- Map, MultiValueMap으로 다중 파라미터 조회 가능하다.
2.@ModelAttribute — 폼 데이터 → 객체 자동 매핑
- 주로 POST방식의 HTML 폼 전송 시 사용된다. (예시: name과 age 필드를 가진 Tutor 클래스에 자동으로 값을 넣어줌)
- 클래스의 필드명과 요청 파라미터명이 같을 경우 Setter로 자동 주입한다.
- @ModelAttribute 생략 가능해도 Spring이 기본 타입(String, int 등) 제외한 객체에는 자동 적용된다.
- Setter 없거나 타입 불일치 시 데이터 주입이 실패한다.
3.@RequestBody - HTTP Body에 담긴 JSON 받기
- JSON이나 텍스트 데이터를 요청 본문(body)에 직접 담아 서버로 전송할 때 사용한다.
- API 설계 시 POST, PUT, PATCH 방식에 유용하다.
- 생략하면 Spring이 @ModelAttribute로 오해할 수 있으니 생략 금지!
- 반드시 Content-Type: application/json 헤더가 있어야 변환 가능하다.
4.@ResponseBody - 데이터를 직접 응답으로 반환
- 웹 페이지(HTML)을 띄우는 대신 서버에서 처리된 데이터(JSON 등)을 직접 반환할 때 사용한다.
- View를 거치지 않고 반환값이 그대로 HTTP 응답 본문에 들어간다.
- @ResponseBody를 붙이면 HttpMessageConverter가 작동해서 자바 객체를 JSON으로 자동 변환한다.
- @RestController = @Controller + @ResponseBody
5. HttpMessageConverter - 자동 변환 도구
- 요청: JSON → Object (@RequestBody, Header 기반 매핑)
- 응답: Object → JSON (@ResponseBody, Accept Header 기반)
- 대표 구현체: MappingJackson2HttpMessageConverter (Jackson 라이브러리를 통해 자바 객체 ↔ JSON 변환 지원)
Spring 응답 데이터
1. 정적 리소스 (Static Resource)
- HTML, CSS, JS, 이미지 등의 파일을 그대로 클라이언트에게 전달한다.
- src/main/resources/static/hello.html → 브라우저에서 http://localhost:8080/hello.html로 접근 가능하다.
2. View Template (서버 사이드 렌더링, SSR)
- 서버가 데이터를 HTML 템플릿에 동적으로 삽입하여 페이지를 만들어주는 방식이다.
- 컨트롤러에서 return "view명" 하면 해당 HTML을 찾아서 보여준다.
- Model 객체를 통해 데이터를 전달하고, 템플릿에서 ${data}처럼 사용한다.
- 주의사항: @Controller와 String return → ViewResolver가 동작하여 HTML 반환한다.
@ResponseBody를 붙이면 그 문자열 그대로 브라우저에 표시 (HTML 아님)
3. HTTP Message Body 직접 응답
- HTML페이지 대신 데이터(JSON, TEXT 등)를 직접 응답 본문에 담아 전송한다. (주로 REST API에서 사용)
- 주요 방식
| 방식 | 설명 | 예시 |
| 1. HttpServletResponse | 서블릿 방식으로 직접 문자열을 작성 | response.getWriter().write("data"); |
| 2. ResponseEntity | 응답 본문 + 상태 코드 동적 설정 가능 | return new ResponseEntity<>("data", HttpStatus.OK); |
| 3. @ResponseBody | 객체를 자동으로 JSON으로 변환하여 응답 | @ResponseBody public Tutor getTutor() { return new Tutor("wonuk", 100); } |
- 응답 상태 코드 설정
고정 상태 코드: @ResponseStatus(HttpStatus.OK)
동적 상태 코드: new ResponseEntity<>(..., HttpStatus.BAD_REQUEST) 등
Layered Architecture
Layered Architecture란 Application을 여러 계층으로 나눠서 각 계층이 자기 역할만 수행하도록 만드는 구조다.
유지보수가 쉽고, 코드 재사용이 가능하며 협업에 유리하다!
| 계층 | 책임 | 어노테이션 |
| Controller (Presentation Layer) | 사용자 요청 받기, 응답하기 | @Controller, @RestController |
| Service (Business Layer) | 비즈니스 로직 처리 | @Service |
| Repository (Data Access Layer) | DB 접근 및 관리 | @Repository |
※ DTO란?
Data Transfer Object는 계층 간 데이터 전달을 담당하는 객체이다. (중간 다리 역할)
DTO를 사용하면 필요한 정보만 선택적으로 전달이 가능하고, 유지보수성이 향상된다.
Entity vs DTO 차이
| 항목 | Entity | DTO |
| 목적 | DB 테이블과 매핑 | 데이터 전달 전용 |
| 위치 | Repository에서 사용 | Controller ↔ Service 간 사용 |
| 위험성 | 외부 노출 시 민감 정보 위험 | 필요한 데이터만 담아 안전 |
마치며

오늘은 API 명세서도 만들어보고 어떤 식으로 구현할지 고심해봤고, 배운 것들을 토대로 실전 감각을 끌어올리는 시간이었다. 단순히 이론을 외우는 게 아니라 코드의 흐름을 보며 왜 이렇게 사용하는지 고민할 수 있었다. 지금은 예시를 따라가고 수강하며 들은 것들을 그저 복사 붙여넣기 하는 것처럼 코드를 치고 있는데, 어느 정도 구조가 만들어지면 주석을 달아보며 하나하나씩 제대로 정리해서 내 것으로 만들고 싶다. 배운 것들을 사용하고 어떤 프로젝트를 진행해갈 때 더 몰입하고 흥미를 느끼는 것 같다. 내일은 더 집중력 있게 시간들을 보내고 싶다.