안녕하세요 선생님. 문의드리고 싶은 내용이 있는데 글을 올릴 곳이 마땅치 않아 여기에 문의드립니다. 저는 현재 DBA 로 근무 중인데 SQL 작성 능력을 키우고 싶은데 혹시 선생님의 '데이터 분석 SQL Fundamentals' 강의가 DBA 업무 쿼리 작성에 도움이 될 수 있는 강의일까요? 일을 해보니 join, grouping 이 쉬운것 같으면서도 다양한 쿼리 작성 시에 자유자재로 안써지는 편이라 아직 공부가 덜 된것 같아서요. 일/주/월 사이즈 구하는 쿼리나 기타 여러 조건이 결합된 오브젝트 조회 쿼리를 주로 작성할 필요가 있고, 앞으로 튜닝 업무를 위한 튜닝 공부를 할 때도 기본 이상의 SQL 작성 능력이 필요하다고 판단되서 문의드립니다. 그리고 선생님의 ORACLE 아키텍쳐 강의는 출퇴근 간에 잘 듣고 있는데 혹시 SQL 튜닝 강의는 하실 예정이 있으실까요? 감사합니다.
datagrip에서 복구 하려고 하면 postgre 관련 cli 가 필요합니다. 아래와 같이 우선 실행 $ brew install libpq $ echo 'export PATH="/opt/homebrew/opt/libpq/bin:$PATH"' >> ~/.zshrc $ source ~/.zshrc $ psql --version psql (PostgreSQL) 15.2 Path to pg_restore 에서 CMD+SHIFT+G 눌러서 brew로 설치한 디렉토리로 이동 이후 복구 하면 됩니다
안녕하세요. 정리 강의를 들으면서 Filter를 스프링 빈으로 등록하는 방법을 따라하다 버전 차이인지, 스프링 부트의 설정 차이인지, 에러가 발생하더군요. The bean 'logFilter', defined in class path resource [hello/springcoremvc26/config/FilterConfig.class], could not be registered. A bean with that name has already been defined in file [/project/java/spring/spring-core-mvc2-6/out/production/classes/hello/springcoremvc26/web/filter/LogFilter.class] and overriding is disabled. 이유를 읽어보니, logFilter가 이미 빈으로 등록되어서 중복 등록이 안된다고 써져있었습니다. 그래서 이에 대해 찾아본 결과를 공유하고자 글을 작성합니다. 아래 글은 제 블로그 에도 정리되어 있습니다. (홍보...ㅎㅎ..) @Configuration LogFilter - 로그 필터 @Slf4j public class LogFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LogFilter init()"); } @Override public void destroy() { log.info("LogFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LogFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LogFilter doFilter End", requestURI); } } } LoginCheckFilter - 로그인 필터 @Slf4j public class LoginCheckFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LoginCheckFilter init()"); } @Override public void destroy() { log.info("LoginCheckFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LoginFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LoginFilter doFilter End", requestURI); } } } FilterConfig - Filter 등록 @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<Filter> logFilter() { FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(); bean.setFilter(new LogFilter()); bean.setOrder(1); bean.addUrlPatterns("/*"); return bean; } @Bean public FilterRegistrationBean<Filter> loginCheckFilter() { FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(); bean.setFilter(new LoginCheckFilter()); bean.setOrder(2); bean.addUrlPatterns("/*"); return bean; } } 실행 결과 [/] LogFilter doFilter Start [/] LoginFilter doFilter Start [/] LoginFilter doFilter End [/] LogFilter doFilter End 특징 강의에서 나온 방법입니다. 설정을 위한 별개의 파일( @Configuration 이 붙은 객체)이 필요합니다. setOrder() 를 통해 순서를 정할 수 있습니다. addUrlPatterns() 을 통해 베이스 URL 및 Whitelist를 설정할 수 있습니다. @Component LogFilter @Slf4j @Component @Order(1) public class LogFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LogFilter init()"); } @Override public void destroy() { log.info("LogFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LogFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LogFilter doFilter End", requestURI); } } } LoginCheckFilter @Slf4j @Component @Order(2) public class LoginCheckFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LoginCheckFilter init()"); } @Override public void destroy() { log.info("LoginCheckFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LoginFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LoginFilter doFilter End", requestURI); } } } 실행 결과 [/] LogFilter doFilter Start [/] LoginFilter doFilter Start [/] LoginFilter doFilter End [/] LogFilter doFilter End 특징 컴포넌트 스캔을 이용하기 때문에 설정을 위한 별개의 파일이 필요하지 않습니다. @Order 애노테이션을 이용해 순서를 설정할 수 있습니다. 기본 URL Pattern이 /* 이며 설정할 수 없습니다. @WebFilter + @ServletComponentScan LogFilter @Slf4j @WebFilter public class LogFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LogFilter init()"); } @Override public void destroy() { log.info("LogFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LogFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LogFilter doFilter End", requestURI); } } } LoginCheckFilter @Slf4j @WebFilter public class LoginCheckFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LoginCheckFilter init()"); } @Override public void destroy() { log.info("LoginCheckFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LoginFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LoginFilter doFilter End", requestURI); } } } MainApplication @ServletComponentScan @SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(SpringCoreMvc26Application.class, args); } } 실행 결과 [/] LogFilter doFilter Start [/] LoginFilter doFilter Start [/] LoginFilter doFilter End [/] LogFilter doFilter End 특징 설정을 위한 별개의 파일이 필요하지 않습니다. 대신, 애플리케이션 실행되는 메인 객체위에 @ServletComponentScan 을 사용해야 합니다. @Order 를 이용한 순서 등록을 사용할 수 없습니다. 각 필터에 대한 순서를 보장할 수 없습니다. @WebFilter 의 value 나 urlPatterns 파라미터를 이용해 whitelist 방식으로 베이스 URL을 설정할 수 있습니다. @WebFilter("/filter/*") @WebFilter({"/login", "/items"}) @WebFilter(urlPatterns = "/filter/*") @WebFilter(urlPatterns = {"/login", "/items"}) @WebFilter + @Component LogFilter @Slf4j @WebFilter @Component @Order(1) public class LogFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LogFilter init()"); } @Override public void destroy() { log.info("LogFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LogFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LogFilter doFilter End", requestURI); } } } LoginCheckFilter @Slf4j @WebFilter @Component @Order(1) public class LoginCheckFilter implements Filter { @Override public void init( FilterConfig filterConfig ) throws ServletException { log.info("LoginCheckFilter init()"); } @Override public void destroy() { log.info("LoginCheckFilter destroy()"); } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestURI = req.getRequestURI(); log.info("[{}] LoginFilter doFilter Start", requestURI); try { chain.doFilter(request, response); } finally { log.info("[{}] LoginFilter doFilter End", requestURI); } } } 실행 결과 [/] LogFilter doFilter Start [/] LoginFilter doFilter Start [/] LoginFilter doFilter End [/] LogFilter doFilter End 특징 두 번째 방법과 세 번째 방법을 모두 사용하는 방법입니다. 설정을 위한 별개의 파일이 필요하지 않습니다. 컴포넌트 스캔 방식을 사용하기 때문에 @ServletComponentScan 도 필요없습니다. @Order 애노테이션을 이용해 순서를 설정할 수 있습니다. @WebFilter 의 value 나 urlPatterns 파라미터를 이용해 베이스 URL이나 Whitelist 방식으로 설정할 수 있습니다. @WebFilter("/filter/*") @WebFilter({"/login", "/items"}) @WebFilter(urlPatterns = "/filter/*") @WebFilter(urlPatterns = {"/login", "/items"}) 애노테이션이 기본으로 3개가 필요합니다. 정리 결론 필터를 사용하기 위한 4가지 방식을 알아보았습니다. 첫 번째 방법( @Configuration + FilterRegistrationBean )과 두 번째, 세 번째 방법을 합친 네 번째 방법( @WebFilter + @Component ) 중에서 고려하면됩니다. 저의 경우에는 @Configuration + FilterRegistrationBean 을 더 맘에 드는 데, 이유는 Filter Class 위에 애노테이션을 여러개 붙이는 것 보다, 별도의 생성 파일을 하나 만들어서 관리하는게 더 편할 것 같아서였습니다. 하지만, @WebFilter + @Component 은 스프링 빈으로 등록하는 방법 이기에, 다른 스프링 빈을 의존성 주입 받을 수 있습니다. 그런 경우에는 첫 번째 방법으론 할 수 없으니, 네 번째 방법을 사용하면 될 것 같습니다.