이전 회사에서 운영하던 서비스의 게시판에 검색 기능을 추가하려고 했었음. 근데 데이터가 핵 많아서 통합검색을 도입하자니 성능과 비용 이슈가 심각했음. 그래서 백엔드 개발자 분이 검색 범위 제한을 대안으로 제시해주셨었는데 오늘 검색 옵션 지정해서 키워드 검색하는 예제를 장시간 풀면서 그 때 생각이 남 ㅋㅋ 조회를 할 때 AND나 OR 연산을 계속 붙이는 게 검색 범위만 늘리고 성능에는 도움이 안된다는 말을 많이 들었는데 이제는 내가 그걸 고민해야 하는 입장에 놓였음 ㅋㅋㅋ 조건이 많아질 수록 제법 어랴우니까 마니 연습해여지,,
아래 세 가지 방법으로 String Boot Actuator 추가 가능
프로젝트 생성 시 두 번째 단계에서 추가
이미 생성된 프로젝트인 경우, 프로젝트 이름 우클릭 > Spring > Add Starters에서 추가(1번과 같은 팝업이 노출됨)
pom.xml에서 직접 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.properties에 아래 코드 추가
management.endpoints.web.exposure.include=mappings,beans,env
핵심적으로 해야 할 작업만 해주면 나머지는 Framework가 알아서 처리
String Boot Actuator와 마찬가지 방법으로 추가 가능
프로젝트 생성 / Add Starters > JDBC API, Oracle Driver 추가
pom.xml에 직접 추가 - Maven일 경우
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
JdbcTemplate 클래스가 JDBC API를 이용하여 DB 연동을 처리하려면 데이터베이스로부터 커넥션을 얻어야 함. 따라서 JdbcTemplate 객체가 사용할 DataSource를 bean으로 등록해 스프링 컨테이너가 생성하도록 해야 함.
DataSource
를 Spring bean으로 자동 등록
해줌//application.properties
# jdbc
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=c##academy
spring.datasource.password=student
등록이 완료된 Bean은 @Autowired
Annotation으로 사용할 수 있음
@Autowired
private JdbcTemplate jdbctemplate;
Entity
)@Controller
@RequestMapping("/music")
public class QuizController {
@Autowired
private JdbcTemplate jdbctemplate;
@RequestMapping("/insert")
@ResponseBody
public String insert(@ModelAttribute MusicDto dto) {
String sql = "insert into music("
+ "music_no, music_title, music_artist, music_album) "
+ "values(music_seq.nextval, ?, ?, ?)";
Object[] param = {
dto.getMusicTitle(), dto.getMusicArtist(), dto.getMusicAlbum()};
jdbctemplate.update(sql, param);
return "등록 완료";
}
}
List 전체 출력은 아래 두 가지 방식을 사용할 수 있음
@RequestMapping("/list")
@ResponseBody
public String list() {
String sql = "select * from music order by music_no asc";
List<MusicDto> list = jdbctemplate.query(sql, MusicDto.getMapper());
//1. return list.toString()
//2. StringBuffer
StringBuffer buffer = new StringBuffer();
for(MusicDto dto : list) {
buffer.append(dto);
buffer.append("<br>");
}return buffer.toString();
}
실제 서비스들의 검색 기능은 1) 검색 옵션을 선택하고, 2) 검색어를 입력하게 구현된 경우가 많음.
기존에는 검색 조건을 OR 또는 UNION으로 나열해 통합 검색을 구현했다면, 이번에는 검색 범위를 좁힌 다음 일치하는 키워드를 검사하는 방식으로 4단계에 걸쳐 고도화를 진행하고자 함
(단계3 또는 단계4 방식으로 검색 기능을 구현할 것을 권장)
@RequestMapping("/search")
@ResponseBody
public String search(@RequestParam String type, String keyword) {
if(type.equals("name")) {
String sql = "select * from guest_book where"
+ "instr(name, ?) > 0 order by no asc";
//중복 코드
Object[] param = {keyword};
List<GuestBookDto> list = jdbctemplate.query(
sql, GuestBookDto.getMapper(), param);
StringBuffer buffer = new StringBuffer();
for(GuestBookDto dto : list) {
buffer.append(dto);
buffer.append("<br>");
}return buffer.toString();
}else {//type.equals("memo")
String sql = "select * from guest_book"
+ "where instr(memo, ?) > 0 order by no asc";
//중복 코드
Object[] param = {keyword};
List<GuestBookDto> list = jdbctemplate.query(
sql, GuestBookDto.getMapper(), param);
StringBuffer buffer = new StringBuffer();
for(GuestBookDto dto : list) {
buffer.append(dto);
buffer.append("<br>");
}return buffer.toString();
}
}
@RequestMapping("/search2")
@ResponseBody
public String search2(@RequestParam String type, String keyword) {
String sql;
if(type.equals("name")) {
sql = "select * from guest_book"
+ "where instr(name, ?) > 0 order by no asc";
}else {
sql = "select * from guest_book"
+ "where instr(memo, ?) > 0 order by no asc";
}
Object[] param = {keyword};
List<GuestBookDto> list = jdbctemplate.query(
sql, GuestBookDto.getMapper(), param);
StringBuffer buffer = new StringBuffer();
for(GuestBookDto dto : list) {
buffer.append(dto);
buffer.append("<br>");
}return buffer.toString();
}
@RequestMapping("/search3")
@ResponseBody
public String search3(@RequestParam String type, String keyword) {
String sql = "select * from guest_book "
+ "where instr(" + type + ", ?) > 0 order by no asc";
Object[] param = {keyword};
List<GuestBookDto> list = jdbctemplate.query(
sql, GuestBookDto.getMapper(), param);
StringBuffer buffer = new StringBuffer();
for(GuestBookDto dto : list) {
buffer.append(dto);
buffer.append("<br>");
}return buffer.toString();
}
@RequestMapping("/search4")
@ResponseBody
public String search4(@RequestParam String type, String keyword) {
String sql = "select * from guest_book "
+ "where instr(#1, ?) > 0 order by no asc";
sql = sql.replace("#1", type);
Object[] param = {keyword};
List<GuestBookDto> list = jdbctemplate.query(
sql, GuestBookDto.getMapper(), param);
StringBuffer buffer = new StringBuffer();
for(GuestBookDto dto : list) {
buffer.append(dto);
buffer.append("<br>");
}return buffer.toString();
}
Bean
: Spring IoC 컨테이너가 관리하는 자바 객체Spring IoC 컨테이너
: 의존성 객체 모음(Show Properties)Annotation 사용
a. @Component Annotation이 있으면 Spring이 자체적으로 등록해줌
@Controller //Controller임을 Spring에게 알려주기 위해 해당 Annotation 사용
public class ParamController{
//..
}
//Controller.class
//@Controller에 @Component가 있는 것을 확인할 수 있음
//--일부 생략--
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
Bean Configuration 파일에 직접 등록
b. Configuration 클래스 생성 > 클래스 내부에 @Bean 생성 코드 작성
@Configuration
public class HomeConfiguration{
@Bean
public ParamController sampleController(){
return new SampleController;
}
}
IoC
, Inversion of Control)DI
, Dependency Injection)스프링 특: 무조건 스프링을 거쳐야 함(IoC, DI)