<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>클리어링의 잡화점</title>
    <link>https://clearing01.tistory.com/</link>
    <description>이것저것</description>
    <language>ko</language>
    <pubDate>Tue, 30 Jun 2026 19:42:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Clearing</managingEditor>
    <image>
      <title>클리어링의 잡화점</title>
      <url>https://tistory1.daumcdn.net/tistory/5407754/attach/e1646dacff71474fb9a1884812e438bb</url>
      <link>https://clearing01.tistory.com</link>
    </image>
    <item>
      <title>Spring Scheduler 정리 (Spring Scheduler, Quartz)</title>
      <link>https://clearing01.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스케쥴러(Scheduler)란? &lt;br /&gt;특정한&amp;nbsp;시간에&amp;nbsp;등록한&amp;nbsp;작업을&amp;nbsp;자동으로&amp;nbsp;실행시키는&amp;nbsp;것 &lt;br /&gt;&lt;br /&gt;spring에서&amp;nbsp;scheduler는&amp;nbsp;대표적으로&amp;nbsp;2가지&amp;nbsp;방식을&amp;nbsp;제공한다.&amp;nbsp;Spring&amp;nbsp;Scheduler와&amp;nbsp;Quartz &lt;br /&gt;&lt;br /&gt;Spring&amp;nbsp;Scheduler &lt;br /&gt;Spring&amp;nbsp;Framework에서&amp;nbsp;기본으로&amp;nbsp;제공하는&amp;nbsp;Scheduler로&amp;nbsp;추가적인&amp;nbsp;의존(dependency)&amp;nbsp;설정이&amp;nbsp;불필요하다 &lt;br /&gt;@Component와&amp;nbsp;@Scheduled&amp;nbsp;어노테이션을&amp;nbsp;이용하여&amp;nbsp;사용이&amp;nbsp;쉽다. &lt;br /&gt;기본적으로&amp;nbsp;1개의&amp;nbsp;Thread를&amp;nbsp;사용하여&amp;nbsp;동기&amp;nbsp;형식으로&amp;nbsp;진행&amp;nbsp;(@EnableAsync를&amp;nbsp;이용하여&amp;nbsp;비동기&amp;nbsp;형식으로도&amp;nbsp;사용&amp;nbsp;가능) &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676257179602&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
@RequiredArgsConstructor
public class testScheduler {

    private final Job job;

    private final JobLauncher jobLauncher;

//    @Scheduled(cron = &quot;${cron}&quot;)
    @Scheduled(cron = &quot;0/10 * * * * *&quot;)
    public void testJob() {

        try {
            jobLauncher.run(job,
                    new JobParametersBuilder()
                            .addString(&quot;datetime&quot;,
                                    LocalDateTime.now().toString())
                            .toJobParameters()
            );
        } catch (JobExecutionAlreadyRunningException e) {
            log.error(&quot;DAILY_DATA JobExecutionAlreadyRunningException: {}&quot;, e.getMessage());
            throw new RuntimeException(e);
        } catch (JobRestartException e) {
            log.error(&quot;DAILY_DATA JobRestartException: {}&quot;, e.getMessage());
            throw new RuntimeException(e);
        } catch (JobInstanceAlreadyCompleteException e) {
            log.error(&quot;DAILY_DATA JobInstanceAlreadyCompleteException: {}&quot;, e.getMessage());
            throw new RuntimeException(e);
        } catch (JobParametersInvalidException e) {
            log.error(&quot;DAILY_DATA JobParametersInvalidException: {}&quot;, e.getMessage());
            throw new RuntimeException(e);
        }

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Quartz &lt;br /&gt;Quartz는&amp;nbsp;Spring에서&amp;nbsp;기본으로&amp;nbsp;제공해주지&amp;nbsp;않기&amp;nbsp;때문에&amp;nbsp;라이브러리&amp;nbsp;의존(dependency)&amp;nbsp;설정이&amp;nbsp;필요 &lt;br /&gt;(implementation&amp;nbsp;&quot;org.springframework.boot:spring-boot-starter-quartz&quot;) &lt;br /&gt;cron 표현식만 사용 가능하다 &lt;span style=&quot;color: #2c2c2c;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #2c2c2c;&quot;&gt;fixedDelay&lt;/span&gt;&lt;span style=&quot;color: #2c2c2c;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #2c2c2c;&quot;&gt;타입을 보장하지 않아 추가 작업 필요&lt;/span&gt;&lt;span style=&quot;color: #2c2c2c;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대적으로&amp;nbsp;사용법이&amp;nbsp;어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;Quartz 사용 이유 &lt;br /&gt;메모리&amp;nbsp;기반의&amp;nbsp;스케줄러뿐만이&amp;nbsp;아닌&amp;nbsp;DB&amp;nbsp;기반의&amp;nbsp;스케줄러&amp;nbsp;지원하기&amp;nbsp;때문에&amp;nbsp;다중&amp;nbsp;서버 간&amp;nbsp;스케줄링이&amp;nbsp;가능 &lt;br /&gt;즉,&amp;nbsp;DB를&amp;nbsp;기반으로&amp;nbsp;클러스터링(Clustering)&amp;nbsp;기능을&amp;nbsp;제공 &lt;br /&gt;&lt;br /&gt;고가용성&amp;nbsp;(High&amp;nbsp;Availability) &lt;br /&gt;한&amp;nbsp;서버가&amp;nbsp;셧다운&amp;nbsp;되더라도&amp;nbsp;다른&amp;nbsp;서버에&amp;nbsp;의해&amp;nbsp;Job이&amp;nbsp;실행되어&amp;nbsp;다운&amp;nbsp;타임이&amp;nbsp;없음 &lt;br /&gt;&lt;br /&gt;확장성&amp;nbsp;(Scalability) &lt;br /&gt;Quartz&amp;nbsp;설정된&amp;nbsp;서버를&amp;nbsp;구동하면&amp;nbsp;자동으로&amp;nbsp;DB에&amp;nbsp;스케줄&amp;nbsp;인스턴스로&amp;nbsp;등록된다 &lt;br /&gt;셧다운&amp;nbsp;된&amp;nbsp;서버는&amp;nbsp;다른&amp;nbsp;서버에&amp;nbsp;의해서&amp;nbsp;DB에서&amp;nbsp;삭제된다 &lt;br /&gt;&lt;br /&gt;로드&amp;nbsp;밸런싱&amp;nbsp;(Load&amp;nbsp;balancing) &lt;br /&gt;Cluster&amp;nbsp;구성으로&amp;nbsp;여러&amp;nbsp;Job이&amp;nbsp;여러&amp;nbsp;서버에&amp;nbsp;분산되어&amp;nbsp;실행된다 &lt;br /&gt;단,&amp;nbsp;Quartz에서는&amp;nbsp;최소한의&amp;nbsp;구현으로&amp;nbsp;random&amp;nbsp;알고리즘만을&amp;nbsp;제공한다 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Quartz&amp;nbsp;사용&amp;nbsp;용어 &lt;br /&gt;&lt;br /&gt;JobDataMap&amp;nbsp;-&amp;nbsp;스케쥴러에서&amp;nbsp;job이&amp;nbsp;실행될&amp;nbsp;때&amp;nbsp;사용할&amp;nbsp;변수&amp;nbsp;값을&amp;nbsp;전달하는데&amp;nbsp;사용 &lt;br /&gt;&lt;br /&gt;JobDetail&amp;nbsp;-&amp;nbsp;job을&amp;nbsp;실행시키기&amp;nbsp;위한&amp;nbsp;정보를&amp;nbsp;담고있는&amp;nbsp;객체(이름,&amp;nbsp;그룹,&amp;nbsp;jobDataMap&amp;nbsp;속성&amp;nbsp;등을&amp;nbsp;지정) &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Trigger가 job을 수행 할 때 이 정보를 기반으로 실행 &lt;br /&gt;&lt;br /&gt;Trigger&amp;nbsp;-&amp;nbsp;job을&amp;nbsp;실행시킬&amp;nbsp;스케줄링&amp;nbsp;조건&amp;nbsp;(반복&amp;nbsp;횟수,&amp;nbsp;시간&amp;nbsp;등) &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -&amp;gt;&amp;nbsp; SimpleTrigger - 단순 반복 횟수와 실행등을 지정 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;CronTrigger - cron 표현식으로 Trigger를 정의 &lt;br /&gt;&lt;br /&gt;Listener&amp;nbsp;-&amp;nbsp;scheduler의&amp;nbsp;이벤트를&amp;nbsp;받을&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;Quartz에서&amp;nbsp;제공하는&amp;nbsp;인터페이스 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&amp;gt; JobListener - job 실행 전후로 이벤트를 받을 수 있음 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;TriggerListener - trigger가 발생하거나, 실패, 완료하였을 때 이벤트를 받을 수 있음&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/122</guid>
      <comments>https://clearing01.tistory.com/122#entry122comment</comments>
      <pubDate>Mon, 13 Feb 2023 12:05:23 +0900</pubDate>
    </item>
    <item>
      <title>Spring batch + 스케쥴러 사용 예제</title>
      <link>https://clearing01.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;출처) &lt;a href=&quot;https://jojoldu.tistory.com/324?category=902551&quot;&gt;https://jojoldu.tistory.com/324?category=902551&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1670147386101&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;1. Spring Batch 가이드 - 배치 어플리케이션이란?&quot; data-og-description=&quot;Spring Batch In Action이 2011년 이후 개정판이 나오지도 않고 (2019.03 기준), 한글 번역판도 없고, 국내 Spring Batch 글 대부분이 튜토리얼이거나 공식 문서 중 일부분을 짧게 번역한 내용들이라 대용량 시&quot; data-og-host=&quot;jojoldu.tistory.com&quot; data-og-source-url=&quot;https://jojoldu.tistory.com/324?category=902551&quot; data-og-url=&quot;https://jojoldu.tistory.com/324&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cDJelm/hyQMwN1xXO/MaqHMqSmib5ZjiJwzFDwnK/img.png?width=800&amp;amp;height=605&amp;amp;face=0_0_800_605,https://scrap.kakaocdn.net/dn/dj92K1/hyQNSB0ObL/cGF0lnMk3kO2SNAWb15hW0/img.png?width=800&amp;amp;height=605&amp;amp;face=0_0_800_605,https://scrap.kakaocdn.net/dn/bTvEkg/hyQNLCS5OI/Wjw2U2YUNPDPMqZ2Ayt0yk/img.png?width=2360&amp;amp;height=606&amp;amp;face=0_0_2360_606&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/324?category=902551&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jojoldu.tistory.com/324?category=902551&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cDJelm/hyQMwN1xXO/MaqHMqSmib5ZjiJwzFDwnK/img.png?width=800&amp;amp;height=605&amp;amp;face=0_0_800_605,https://scrap.kakaocdn.net/dn/dj92K1/hyQNSB0ObL/cGF0lnMk3kO2SNAWb15hW0/img.png?width=800&amp;amp;height=605&amp;amp;face=0_0_800_605,https://scrap.kakaocdn.net/dn/bTvEkg/hyQNLCS5OI/Wjw2U2YUNPDPMqZ2Ayt0yk/img.png?width=2360&amp;amp;height=606&amp;amp;face=0_0_2360_606');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1. Spring Batch 가이드 - 배치 어플리케이션이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Batch In Action이 2011년 이후 개정판이 나오지도 않고 (2019.03 기준), 한글 번역판도 없고, 국내 Spring Batch 글 대부분이 튜토리얼이거나 공식 문서 중 일부분을 짧게 번역한 내용들이라 대용량 시&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jojoldu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배치란 일괄처리라는 의미를 가지고 있으며 다음의 조건을 만족해야만 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 대용량 데이터 - 배치 어플리케이션은 대량의 데이터를 가져오거나, 전달, 계산하는 등의 처리를 할 수 ​​있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 자동화 - 배치 어플리케이션은 심각한 문제 해결을 제외하고는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;사용자 개입 없이 실행&lt;/b&gt;되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 견고성 - 배치 어플리케이션은 잘못된 데이터를 충돌/중단 없이 처리할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 신뢰성 - 배치 어플리케이션은 무엇이 잘못되었는지를 추적할 수 있어야 한다. (로깅, 알림)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5) 성능 - 배치 어플리케이션은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지정한 시간 안에 처리를 완료&lt;/b&gt;하거나 동시에 실행되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 어플리케이션을 방해하지 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 않도록 수행&lt;/b&gt;되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Job&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; 배치 처리 과정을 하나의 단위로 만들어 표현한 객체이고 여러 Step 인스턴스를 포함하는 컨테이너&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; 실질적인 배치 처리를 저의 하고 제어하는데 필요한 모든 정보가 있는 도메인 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tasklet&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; Step안에서 수행될 비즈니스 로직 전략의 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jobInstance&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; job의 실행 단위이며, job 실행 시 하나의 instance가 생성 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;실패하여&amp;nbsp;재&amp;nbsp;실행할&amp;nbsp;경우&amp;nbsp;해당&amp;nbsp;instance에&amp;nbsp;관련된&amp;nbsp;데이터만&amp;nbsp;처리 &lt;br /&gt;&lt;br /&gt;jobParameters&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; jobInstance에 전달되는 매개변수로 jobInstance를 구분하는 객체로 사용 &lt;br /&gt;&lt;br /&gt;JobLauncher&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; job과 jobParameters를 사용하여 job을 실행하는 객체 &lt;br /&gt;&lt;br /&gt;Execution&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; jopExecution, stepExecution이 있으며 실행 시도에 대한 객체를 나타내며 &lt;br /&gt;&amp;nbsp; &amp;nbsp; 상태,시작시간, 종료시간, 생성시간 등의 정보를 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quartz는 스케줄러의 역할&lt;span style=&quot;color: #555555;&quot;&gt;이지, Batch와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;대용량 데이터 배치 처리에 대한 기능을 지원하지 않는다.&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;반대로 Batch 역시 Quartz의 다양한 스케줄 기능을 지원하지 않아서 보통은 Quartz + Batch를 조합해서 사용한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #555555;&quot;&gt;정해진 스케줄마다 Quartz가 Spring Batch를 실행하는 구조라고 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;build.gradle에 dependency 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;sml&quot;&gt;&lt;code&gt;dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
   implementation 'org.springframework.boot:spring-boot-starter-batch'
   implementation 'org.springframework.boot:spring-boot-starter-quartz'
   compileOnly 'org.projectlombok:lombok'
   runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
   annotationProcessor 'org.projectlombok:lombok'

   testImplementation 'org.springframework.boot:spring-boot-starter-test'
   testImplementation 'org.springframework.batch:spring-batch-test'

   annotationProcessor 'org.projectlombok:lombok'
   testAnnotationProcessor 'org.projectlombok:lombok'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;resources 폴더 하위 application.yml에 설정&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670147691737&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    driver-class-name: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://localhost:3306/test
    username: root
    password: 1234

  batch:
    jdbc:
      initialize-schema: &quot;always&quot;

      batch.job.enabled: false # CommandLineRunner 설정 해제 // 스케줄러를 사용하기 떄문에 구동시 동작 해제

  jpa:
    hibernate:
      ddl-auto: create&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Job과 Step을 구성하는 Config&lt;/p&gt;
&lt;pre id=&quot;code_1670146851293&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.batch.jobs;

...

@Configuration
@RequiredArgsConstructor
@Transactional
public class TutorialConfig {

    @Autowired
    @Qualifier(&quot;JpaUserRepository&quot;)
    UserRepository userRepository;

    private final JobBuilderFactory jobBuilderFactory; // Job 빌더 생성용
    private final StepBuilderFactory stepBuilderFactory; // Step 빌더 생성용

    // JobBuilderFactory를 통해서 tutorialJob을 생성
    @Bean
    public Job tutorialJob() {
        return jobBuilderFactory.get(&quot;tutorialJob&quot;)
                .start(tutorialStep())  // Step 설정
                .build();
    }

    // StepBuilderFactory를 통해서 tutorialStep을 생성
    @Bean
    public Step tutorialStep() {

        return stepBuilderFactory.get(&quot;tutorialStep&quot;)
                .tasklet(new TutorialTasklet(userRepository)) // Tasklet 설정
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;tasklet으로 step을 구성 -&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;구성된 step으로 job을 구성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RequiredArgsConstructor 어노테이션에 의해 생성자 주입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로직을 실행하는 Tasklet&lt;/p&gt;
&lt;pre id=&quot;code_1670146943778&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.batch.tasklets;

...

@Slf4j
@RequiredArgsConstructor
public class TutorialTasklet implements Tasklet {


    final UserRepository userRepository;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        log.info(&quot;executed tasklet !!&quot;);

        insertUser();

        return RepeatStatus.FINISHED;
    }

    public void insertUser(){
        UserEntity user = new UserEntity();
        user.setPw(&quot;1234&quot;);
        user.setName(&quot;1111&quot;);

        System.out.println(user);
        System.out.println(userRepository);

        userRepository.save(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 구성한 Batch를 실행하는 스케쥴러&lt;/p&gt;
&lt;pre id=&quot;code_1670146975783&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.batch.schedulers;

...

import java.time.LocalDateTime;

@Component
@RequiredArgsConstructor
public class TutorialScheduler {

    private final Job job;  // tutorialJob
    private final JobLauncher jobLauncher;

    // 5초마다 실행
    @Scheduled(fixedDelay = 5 * 1000L)
    public void executeJob () {
        try {
            jobLauncher.run(
                    job,
                    new JobParametersBuilder()
                            .addString(&quot;datetime&quot;, LocalDateTime.now().toString())
                            .toJobParameters()  // job parameter 설정
            );
        } catch (JobExecutionException ex) {
            System.out.println(ex.getMessage());
            ex.printStackTrace();
        }
    }

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/121</guid>
      <comments>https://clearing01.tistory.com/121#entry121comment</comments>
      <pubDate>Tue, 6 Dec 2022 19:58:20 +0900</pubDate>
    </item>
    <item>
      <title>스프링 부트 - JPA 연동</title>
      <link>https://clearing01.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;build.gradle에 dependency 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;sml&quot;&gt;&lt;code&gt;dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
   implementation 'org.springframework.boot:spring-boot-starter-web'
   developmentOnly 'org.springframework.boot:spring-boot-devtools'
   runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
   testImplementation 'org.springframework.boot:spring-boot-starter-test'

   implementation 'javax.servlet:jstl'
   implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;resources 폴더 하위 application.properties에 설정&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;## MariaDB
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=1234

server.port=8088

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring.jpa.show-sql&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; console 창에 jpa의 sql문을 볼지 안 볼지 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;spring.jpa.hibernate.ddl-auto&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;create: 기존테이블 삭제 후 다시 생성 (DROP + CREATE)&lt;/li&gt;
&lt;li&gt;create-drop: create와 같으나 종료 시점에 테이블 DROP&lt;/li&gt;
&lt;li&gt;update: 변경분만 반영(운영 DB에서는 사용하면 안 됨)&lt;/li&gt;
&lt;li&gt;validate: 엔티티와 테이블이 정상 매핑되었는지만 확인&lt;/li&gt;
&lt;li&gt;none: 사용하지 않음(사실상 없는 값이지만 관례상 none이라고 한다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;users 테이블과 매핑할 UserEntity&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.example.jpatest.entity;

import javax.persistence.*;

@Entity(name = &quot;users&quot;)
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name=&quot;id&quot;)
    private Long id;
    @Column(name=&quot;pw&quot;)
    private String pw;
    @Column(name=&quot;name&quot;)
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getPw() {
        return pw;
    }

    public void setPw(String pw) {
        this.pw = pw;
    }
    public String getName() {
        return name;
    }

    public void setName(String username) {
        this.name = username;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserRepository 인터페이스&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;package com.example.jpatest.repository;

import com.example.jpatest.entity.UserEntity;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface UserRepository {
    UserEntity save(UserEntity user);
    void delete(Long id);
    void update(UserEntity user);
    Optional&amp;lt;UserEntity&amp;gt; findById(Long id);
    Optional&amp;lt;UserEntity&amp;gt; findByName(String name);
    List&amp;lt;UserEntity&amp;gt; findAll();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JpaRepository&amp;lt;T, Id&amp;gt;를 상속받아 인터페이스를 구성하여 JpaRepository의 메서드를 그대로 사용하는 것도 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Repository에서는 필요한 기능만을 커스텀하여 사용하기 위해 JpaRepository를 상속받지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 인터페이스를 상속받는 JpaUserRepository&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.example.jpatest.repository;

import com.example.jpatest.entity.UserEntity;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;

@Component(&quot;JpaUserRepository&quot;)
public class JpaUserRepository implements UserRepository{

    private final EntityManager em;

    public JpaUserRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public UserEntity save(UserEntity user) {
        em.persist(user);
        return user;
    }

    @Override
    public void delete(Long id) {
        UserEntity user = em.find(UserEntity.class, id);
        em.remove(user);
    }

    @Override
    public void update(UserEntity user) {
        em.createQuery(&quot;update users u set u.name= :name where u.id= :id&quot;)
                .setParameter(&quot;name&quot;,user.getName())
                .setParameter(&quot;id&quot;,user.getId())
                .executeUpdate();
    }

    @Override
    public Optional&amp;lt;UserEntity&amp;gt; findById(Long id) {
        UserEntity user = em.find(UserEntity.class, id);
        return Optional.ofNullable(user);
    }

    @Override
    public Optional&amp;lt;UserEntity&amp;gt; findByName(String name) {
        List&amp;lt;UserEntity&amp;gt; result = em.createQuery(&quot;select u from users u where u.name= :name&quot;, UserEntity.class)
        .setParameter(&quot;name&quot;, name)
        .getResultList();

        return result.stream().findAny();
    }

    @Override
    public List&amp;lt;UserEntity&amp;gt; findAll() {
      List&amp;lt;UserEntity&amp;gt; result = em.createQuery(&quot;select u from users u&quot;, UserEntity.class)
                .getResultList();
        return result;
    }



}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EntitiyManager를 통해 쿼리문을 커스텀하여 사용&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/120</guid>
      <comments>https://clearing01.tistory.com/120#entry120comment</comments>
      <pubDate>Mon, 5 Dec 2022 09:25:46 +0900</pubDate>
    </item>
    <item>
      <title>스프링 부트 - MyBatis 연동</title>
      <link>https://clearing01.tistory.com/119</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Gradle을 이용하여 빌드를 진행하기 때문에 그에 맞춰 필요한 설정을 해준다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;build.gradle에 dependency 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670139069937&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
	runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

	implementation 'javax.servlet:jstl'
	implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;resources 폴더 하위 application.properties에도 설정을 진행한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;## 사용할 포트번호
server.port=8088

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

## MariaDB
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=1234

## mybatis
mybatis.mapper-locations=classpath:mybatis/mappings/*.xml
mybatis.type-aliases-package=com.example.demo.member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mybatis.mapper-locations=classpath:mybatis/mappings/*.xml&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; sql문을 작성하는 mapping 파일의 위치 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;mybatis.type-aliases-package=com.example.demo.member&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; mapper에 작성할 파일의 이름이 길어지기 때문에 별칭 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member-mapping.xml 파일&lt;/p&gt;
&lt;pre id=&quot;code_1670139538266&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE mapper PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&amp;gt;
&amp;lt;mapper namespace=&quot;MemberService&quot;&amp;gt;

	&amp;lt;insert id=&quot;insertMember&quot;&amp;gt;
		INSERT INTO MEMBER VALUES(#{mid},#{mpw},#{mname})
	&amp;lt;/insert&amp;gt;
	
	&amp;lt;update id=&quot;updateMember&quot;&amp;gt;
		UPDATE MEMBER SET MNAME=#{mname} WHERE MID=#{mid}
	&amp;lt;/update&amp;gt;
	
	&amp;lt;delete id=&quot;deleteMember&quot;&amp;gt;
		&amp;lt;![CDATA[
		DELETE FROM MEMBER WHERE MID=#{mid}
		]]&amp;gt;
	&amp;lt;/delete&amp;gt;
	
	&amp;lt;select id=&quot;selectOneMember&quot; resultType=&quot;MemberVO&quot;&amp;gt;
		SELECT * FROM MEMBER WHERE MID=#{mid}
	&amp;lt;/select&amp;gt;
	
 
	&amp;lt;select id=&quot;selectAllBoard&quot; resultType=&quot;MemberVO&quot;&amp;gt;
		SELECT * FROM MEMBER
	&amp;lt;/select&amp;gt;


&amp;lt;/mapper&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mapper namespace = 해당 파일을 MemberService라 명명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mapper namespace의 MemberService 앞에 별칭으로 설정해 놓은 com.example.demo.member가 생략되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member-mapping.xml(MemberService) 파일을 사용하는 memberDAO&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;package com.example.demo.member.impl;

import com.example.demo.member.MemberVO;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository(&quot;memberDAO&quot;)
public class MemberDAO {

    @Autowired
    private SqlSessionTemplate mybatis;

    public void insertMember(MemberVO vo) {
        System.out.println(&quot;회원가입 로그&quot;);
        mybatis.insert(&quot;MemberService.insertMember&quot;, vo);
    }

    void updateMember(MemberVO vo) {

        mybatis.update(&quot;MemberService.updateMember&quot;, vo);
    }

    void deleteMember(MemberVO vo) {

        mybatis.delete(&quot;MemberService.deleteMember&quot;, vo);
    }

    MemberVO selectOneMember(MemberVO vo) {

        return mybatis.selectOne(&quot;MemberService.selectOneMember&quot;, vo);
    }

    public List&amp;lt;MemberVO&amp;gt; selectAllMember(MemberVO vo) {

        return mybatis.selectList(&quot;MemberService.selectAllMember&quot;, vo);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 내의 인자 값은 namespace에 명명한 이름과 일치하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MemberService 인터페이스에 @Mapper 설정&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Mapper
public interface MemberService {
    void insertMember(MemberVO vo);
    void deleteMember(MemberVO vo);
    void updateMember(MemberVO vo);
    MemberVO selectOneMember(MemberVO vo);
    List&amp;lt;MemberVO&amp;gt; selectAllMember(MemberVO vo);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 인터페이스를 상속받는 ServiceImpl&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;package com.example.demo.member.impl;

import com.example.demo.member.MemberService;
import com.example.demo.member.MemberVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service(&quot;memberServiceImpl&quot;)
public class MemberServiceImpl implements MemberService {

    @Autowired
    private MemberDAO memberDAO;

    @Override
    public void insertMember(MemberVO vo) {
        memberDAO.insertMember(vo);
    }

    @Override
    public void deleteMember(MemberVO vo) {
        memberDAO.deleteMember(vo);
    }

    @Override
    public void updateMember(MemberVO vo) {
        memberDAO.updateMember(vo);
    }

    @Override
    public MemberVO selectOneMember(MemberVO vo) {
        return memberDAO.selectOneMember(vo);
    }

    @Override
    public List&amp;lt;MemberVO&amp;gt; selectAllMember(MemberVO vo) {
        return memberDAO.selectAllMember(vo);
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/119</guid>
      <comments>https://clearing01.tistory.com/119#entry119comment</comments>
      <pubDate>Sun, 4 Dec 2022 16:50:24 +0900</pubDate>
    </item>
    <item>
      <title>스프링 부트 - Validator 검증</title>
      <link>https://clearing01.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Validator는 사용자가 보낸 데이터에 대한 유효성 검사를 진행하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 유효성 검사는 두 단계로 진행하는 것이 보편적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 클라이언트(브라우저, 사용자) : JS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 서버 : 파라미터 값을 검증&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 이러한 검증방법이 회사마다 사용하는 곳마다 많이 달랐기 때문에 유지보수에 불리했지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에서는 Validator 인터페이스를 구현해놓았기 때문에 개발자들이 보다 일관된 코드를 작성할 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 입력한 데이터를 확인하기 위한 Validator 예시&lt;/p&gt;
&lt;pre id=&quot;code_1666751401448&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kim.springboot;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

// 유효성 검사 객체
public class DataValidator implements Validator{

	@Override
	public boolean supports(Class&amp;lt;?&amp;gt; clazz) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void validate(Object target, Errors errors) {
		// Object target : 사용자의 입력 값(커맨드 객체)을 검증하기 위해, 
		// 파라미터로 받을 수 있게 구현한 부분
		// -&amp;gt; 디자인 패턴을 활용한 예: 부모에게 자식을 대입 가능
		DataDTO dto = (DataDTO)target;
		
		// 커맨드 객체에 저장된 값을 추출
		String writer = dto.getWriter();
		if(writer==null || writer.trim().isEmpty()) { 
		// ★ null 체크 순서 유의 !! null 이면 isEmpty가 실행이 안됨
			System.out.println(&quot;로그: DataValidator: validate 메서드: 작성자 null or empty&quot;);
			errors.rejectValue(&quot;writer&quot;, &quot;error&quot;);
		}
		
		String content = dto.getContent();
		if(content==null || content.trim().isEmpty()) { 
			System.out.println(&quot;로그: DataValidator: validate 메서드: 내용 null or empty&quot;);
			errors.rejectValue(&quot;content&quot;, &quot;error&quot;);
		}
		
		
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. errors 객체에 에러 내용을 저장했는데, 별도의 return을 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. .validate()을 수행할 때에, errors 객체를 &quot;참조 변수&quot;로 활용하기 때문이다&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&amp;gt; 모델 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Validator를 사용하는 컨트롤러&lt;/p&gt;
&lt;pre id=&quot;code_1666751491112&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kim.springboot;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
	
	@RequestMapping(&quot;/&quot;)
	public @ResponseBody String root() {
		return &quot;Validator 실습&quot;;
	}
	
	@RequestMapping(&quot;/insertPage&quot;)
	public String insertPage() {
		return &quot;insertPage&quot;;
	}
	
	@RequestMapping(&quot;/insert&quot;)
	public String insert(@ModelAttribute(&quot;dto&quot;)DataDTO dto, BindingResult result) {
		// 데이터 유지를 위해 @ModelAttribute 사용
		String viewName = &quot;boardPage&quot;;
		
		System.out.println(&quot;로그: TestController: insert 메서드: dto: &quot;+dto);
		DataValidator validator= new DataValidator();
		validator.validate(dto, result); // result는 참조변수 ★
		if(result.hasErrors()) { // 에러가 발견되었다면,
			viewName=&quot;insertPage&quot;;
		}
		
		return viewName;
	}
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 입력을 받는 insertPage&lt;/p&gt;
&lt;pre id=&quot;code_1666751616799&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
&amp;lt;title&amp;gt;Insert title here&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;form action=&quot;/insert&quot; method=&quot;post&quot;&amp;gt;
	작성자: &amp;lt;input type=&quot;text&quot; name=&quot;writer&quot; value=&quot;${dto.writer}&quot;&amp;gt; &amp;lt;br&amp;gt;
	내용: &amp;lt;input type=&quot;text&quot; name=&quot;content&quot; value=&quot;${dto.content}&quot;&amp;gt; &amp;lt;br&amp;gt;
	&amp;lt;input type=&quot;submit&quot; value=&quot;글 작성하기&quot;&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=====================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 화면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;insertPage&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OmlQB/btrPvwQffWF/SokAk8GoDXlKz9mYuCEupk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OmlQB/btrPvwQffWF/SokAk8GoDXlKz9mYuCEupk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OmlQB/btrPvwQffWF/SokAk8GoDXlKz9mYuCEupk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOmlQB%2FbtrPvwQffWF%2FSokAk8GoDXlKz9mYuCEupk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;120&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=====================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/insert에 의해 이동된 boardPage&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg6IDD/btrPzMxW5xY/W7oRnXvVt6uBTaHKzNamdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg6IDD/btrPzMxW5xY/W7oRnXvVt6uBTaHKzNamdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg6IDD/btrPzMxW5xY/W7oRnXvVt6uBTaHKzNamdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg6IDD%2FbtrPzMxW5xY%2FW7oRnXvVt6uBTaHKzNamdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;283&quot; height=&quot;82&quot; data-origin-width=&quot;283&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=====================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Validator 조건에 의해 작성자나 내용이 null 또는 비어있다면 해당 페이지에 데이터를 유지한 채 이동&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF119G/btrPys1ww4a/hvNzV6zYZFyPVTaJhMJH8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF119G/btrPys1ww4a/hvNzV6zYZFyPVTaJhMJH8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF119G/btrPys1ww4a/hvNzV6zYZFyPVTaJhMJH8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF119G%2FbtrPys1ww4a%2FhvNzV6zYZFyPVTaJhMJH8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;126&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/118</guid>
      <comments>https://clearing01.tistory.com/118#entry118comment</comments>
      <pubDate>Wed, 26 Oct 2022 11:37:44 +0900</pubDate>
    </item>
    <item>
      <title>스프링 부트 - 의존 주입</title>
      <link>https://clearing01.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;의존 주입은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) new를 직접 수행해서 바로 사용 (강한 결합)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 미리 만들어진 객체를 할당받아서 사용 =&amp;gt; DI 의존성 주입 (약한 결합)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체를 생성해주며 라이프사이클(생명주기)을 관리하고 의존 주입을 해준다 == 컨테이너 -&amp;gt; IoC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유지보수 용이 ==&amp;nbsp;낮은&amp;nbsp;결합도,&amp;nbsp;높은&amp;nbsp;응집도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[의존 주입 방법]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) .xml&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 자바 코드 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) @&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@을 이용한 의존 주입 예시 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 예시 클래스들은 미리 작성해 둠&lt;/p&gt;
&lt;pre id=&quot;code_1666593895094&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kim.springboot.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 스프링 컨테이너의 설정 파일 역할
public class Config {
	// 설정 파일
	
	@Bean // 객체화 공식
	public Leader leaderA() {
		// Setter 주입
		Leader leader1 = new Leader();
		leader1.setName(&quot;티모&quot;);
		leader1.setMember(new MemberA());
		return leader1;
	}
	
	@Bean(name=&quot;kim&quot;)
	public Leader leaderB() {
		return new Leader(&quot;아리&quot;,new MemberA());
	}
	
	@Bean
	public MemberA memberA() {
		return new MemberA();
	}
	
	@Bean
	public MemberB memberB() {
		return new MemberB();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설정 파일을 사용하는 메인 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(@SpringBootApplication 사용 안 함)&lt;/p&gt;
&lt;pre id=&quot;code_1666593976277&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kim.springboot;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;

import com.kim.springboot.test.Config;
import com.kim.springboot.test.Leader;
import com.kim.springboot.test.Member;

//@SpringBootApplication
public class TestApplication {

	public static void main(String[] args) {
	//	SpringApplication.run(TestApplication.class, args);
		
		// 1. IoC 제공해줄 스프링 컨테이너 생성하기
		ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
		// 싱글톤으로 객체 생성 및 관리됨
		
		// 2. @Bean 등록한 객체 사용해보기
		Leader leader1 = (Leader) ac.getBean(&quot;leaderA&quot;);
		leader1.hello();
		// @Configuration으로 해당 파일이 설정파일이라는것을 인식 생성되어 있는 클래스들을 Config파일에 설정 
		// -&amp;gt; @Bean으로 컨테이너에 의해 각 클래스들 객체화
		// -&amp;gt; Config 설정 파일에서 설정한 내용들이 ac에 주입 
		// -&amp;gt; ac에서 객체화된 leaderA를 가져와 leader1에 주입 
		
		Leader leader2 = (Leader) ac.getBean(&quot;kim&quot;);
		leader2.hello();
		//
		
		Member member = ac.getBean(&quot;memberB&quot;,Member.class);
		member.hello(&quot;main에서 작성&quot;);
		//
		
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ApplicationContext&amp;nbsp;ac&amp;nbsp;=&amp;nbsp;new&amp;nbsp;AnnotationConfigApplicationContext(???); &lt;br /&gt;=&amp;gt;&amp;nbsp;디자인&amp;nbsp;패턴&amp;nbsp;중&amp;nbsp;하나의&amp;nbsp;예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=====================================================================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 2 설정 파일 대신 실행될 컨트롤러&lt;/p&gt;
&lt;pre id=&quot;code_1666594239925&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kim.springboot.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
	@Autowired
	Leader leader1;
	@Autowired
	Leader leader2;
	@Autowired
	@Qualifier(&quot;memberB&quot;)
	Member member;
	
	@RequestMapping(&quot;/&quot;)
	public @ResponseBody String root() {
		leader1.hello();
		leader1.setMember(member);
		leader1.hello();
		
		if(leader1 == leader2) {
			System.out.println(&quot;동일한 객체입니다.&quot;);
		}
		else {
			System.out.println(&quot;동일하지 않습니다.&quot;);
		}
		
		return &quot;루트(표지) 페이지&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@SpringBootApplication 사용&lt;/p&gt;
&lt;pre id=&quot;code_1666594254245&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.kim.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Test02Application {

	public static void main(String[] args) {
		SpringApplication.run(Test02Application.class, args); // 내장된 톰캣 실행
		// 1. main 메서드 실행
		// 2. @SpringBootApplication의 영향으로 @ 스캔 및 등록
		// 3. 10번 라인에 의해 내장 톰캣 실행
		// 4. ApplicationContext 생성 == 스프링 컨테이너 실행
		//		-&amp;gt; URL로 요청 입력시 RequestMapping에 의해 작성한 메서드가 호출되는 방식
	}

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- @SpringBootApplication &lt;br /&gt;Bean(객체)들을&amp;nbsp;생성할 때,&amp;nbsp;싱글톤을&amp;nbsp;유지할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;설정&amp;nbsp;및&amp;nbsp;관리 &lt;br /&gt;각각의&amp;nbsp;객체들이&amp;nbsp;생성되면&amp;nbsp;용이한&amp;nbsp;순간을&amp;nbsp;추측하여&amp;nbsp;메모리를&amp;nbsp;관리 &lt;br /&gt;.jar&amp;nbsp;파일들도&amp;nbsp;생성&amp;nbsp;및&amp;nbsp;설정&amp;nbsp;관리 &lt;br /&gt;@&amp;nbsp;지정된&amp;nbsp;클래스를&amp;nbsp;스캔해서&amp;nbsp;Bean&amp;nbsp;등록&amp;nbsp;관리&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/117</guid>
      <comments>https://clearing01.tistory.com/117#entry117comment</comments>
      <pubDate>Tue, 25 Oct 2022 09:05:21 +0900</pubDate>
    </item>
    <item>
      <title>스프링 부트 설치 및 시작</title>
      <link>https://clearing01.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트를 사용하게 되면 XML이 아닌 @을 사용하여 빈(Bean) 등록을 위한 설정파일(.xml)이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요해지므로 자바 코드만 보고서도 전체 내용 파악이 가능하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리 관리 또한 pom.xml이 아닌 그레이들(GRADLE)을 사용하게 되면 .xml 대신&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 설정파일을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 부트를 사용하게 되면 @을 사용하여 과도한 xml 설정이 필요 없어져 분석을 용이하게 할 수 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 와스(WAS) 없이 내장된 톰캣 서버 사용이 가능하다. 또한 스트링 부트 스타터가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 라이브러리 관리를 위한 자동 설정을 제공해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[스프링&amp;nbsp;부트&amp;nbsp;설치] &lt;br /&gt;&amp;nbsp;이클립스 추가 설치 or 사이트에서 직접 설치 &lt;br /&gt;&lt;a href=&quot;https://spring.io/tools&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://spring.io/tools&lt;/a&gt;&amp;nbsp;-&amp;gt;&amp;nbsp;.jar&amp;nbsp;다운로드&amp;nbsp;확인&amp;nbsp;-&amp;gt;&amp;nbsp;cmd&amp;nbsp;.jar(==.zip)&amp;nbsp;압축해제 &lt;br /&gt;-&amp;gt; cmd창에서 설치위치로 이동 후 확인 ex) d: -&amp;gt; dir(해당 위치에 있는 파일 확인 가능)&lt;br /&gt;-&amp;gt;&amp;nbsp;java&amp;nbsp;-jar&amp;nbsp;spring-tool&amp;nbsp;후&amp;nbsp;탭키&amp;nbsp;누르면&amp;nbsp;자동완성 &lt;br /&gt;-&amp;gt;&amp;nbsp;sts&amp;nbsp;release에서&amp;nbsp;.exe&amp;nbsp;실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[Spring&amp;nbsp;Starter&amp;nbsp;Project&amp;nbsp;설정] &lt;br /&gt;Name&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;이름 &lt;br /&gt;Type&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;라이브러리 관리 도구 &lt;br /&gt;Package&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;패키징 형식 -&amp;gt; 내장 WAS인 톰캣 서버 사용 시 War 사용 &lt;br /&gt;Java Version&amp;nbsp; &amp;nbsp; &amp;nbsp; 11이상 &lt;br /&gt;Group&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;그룹명 com.kim &lt;br /&gt;Package&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;패키지명 com.kim.springboot&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G1yKD/btrPjGMVyvZ/gsMdp7r5kjiXYQ7EVwZWH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G1yKD/btrPjGMVyvZ/gsMdp7r5kjiXYQ7EVwZWH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G1yKD/btrPjGMVyvZ/gsMdp7r5kjiXYQ7EVwZWH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG1yKD%2FbtrPjGMVyvZ%2FgsMdp7r5kjiXYQ7EVwZWH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;499&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next&amp;nbsp;-&amp;nbsp;&amp;gt;&amp;nbsp;spring&amp;nbsp;web&amp;nbsp;-&amp;gt;&amp;nbsp;우측 하단&amp;nbsp;100%&amp;nbsp;설치될 때까지&amp;nbsp;대기 &lt;br /&gt;=&amp;gt;&amp;nbsp;'spring&amp;nbsp;web'&amp;nbsp;Dependencies&amp;nbsp;주입&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B7Dcl/btrPs4yqRIh/6JNXTRPswaIYjn0KU1nDl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B7Dcl/btrPs4yqRIh/6JNXTRPswaIYjn0KU1nDl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B7Dcl/btrPs4yqRIh/6JNXTRPswaIYjn0KU1nDl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB7Dcl%2FbtrPs4yqRIh%2F6JNXTRPswaIYjn0KU1nDl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;273&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/main/resources -&amp;gt; static 하위(정적파일) ex) html, css, js 등 &lt;br /&gt;src 하위 -&amp;gt; 동적파일 ex) jsp 등 &lt;br /&gt;build.gradle&amp;nbsp;==&amp;nbsp;pom.xml&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장된&amp;nbsp;톰캣&amp;nbsp;서버의&amp;nbsp;기본&amp;nbsp;포트&amp;nbsp;번호는&amp;nbsp;8080 &lt;br /&gt;오라클과&amp;nbsp;함께&amp;nbsp;사용하면&amp;nbsp;충돌&amp;nbsp;발생 &lt;br /&gt;&amp;nbsp;-&amp;gt; application.properties에서 server.port=???로 설정 ex) 8088&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf5k4d/btrPrq24LQ3/T1SFyIOuIyMbAKMeabuEV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf5k4d/btrPrq24LQ3/T1SFyIOuIyMbAKMeabuEV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf5k4d/btrPrq24LQ3/T1SFyIOuIyMbAKMeabuEV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf5k4d%2FbtrPrq24LQ3%2FT1SFyIOuIyMbAKMeabuEV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;171&quot; height=&quot;65&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Spring</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/116</guid>
      <comments>https://clearing01.tistory.com/116#entry116comment</comments>
      <pubDate>Mon, 24 Oct 2022 14:50:11 +0900</pubDate>
    </item>
    <item>
      <title>크롤링 시&amp;nbsp;만나 에러 - NosuchElementException</title>
      <link>https://clearing01.tistory.com/115</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p3Bif/btrO2ocLrEY/8Piggzx93B7qmr4NKA3rk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p3Bif/btrO2ocLrEY/8Piggzx93B7qmr4NKA3rk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p3Bif/btrO2ocLrEY/8Piggzx93B7qmr4NKA3rk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp3Bif%2FbtrO2ocLrEY%2F8Piggzx93B7qmr4NKA3rk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;339&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZxS2Y/btrO0pjqyVc/KtonPqDBDRjCFdMpIdkBn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZxS2Y/btrO0pjqyVc/KtonPqDBDRjCFdMpIdkBn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZxS2Y/btrO0pjqyVc/KtonPqDBDRjCFdMpIdkBn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZxS2Y%2FbtrO0pjqyVc%2FKtonPqDBDRjCFdMpIdkBn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;373&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Java/Exception</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/115</guid>
      <comments>https://clearing01.tistory.com/115#entry115comment</comments>
      <pubDate>Fri, 21 Oct 2022 09:54:18 +0900</pubDate>
    </item>
    <item>
      <title>필터 구현 시 만난 에러 - ORA-00933</title>
      <link>https://clearing01.tistory.com/114</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uhPia/btrOZbe89vU/pkRKIxKSDiOqXuOB65Nbu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uhPia/btrOZbe89vU/pkRKIxKSDiOqXuOB65Nbu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uhPia/btrOZbe89vU/pkRKIxKSDiOqXuOB65Nbu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuhPia%2FbtrOZbe89vU%2FpkRKIxKSDiOqXuOB65Nbu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;393&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Java/Exception</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/114</guid>
      <comments>https://clearing01.tistory.com/114#entry114comment</comments>
      <pubDate>Thu, 20 Oct 2022 09:53:11 +0900</pubDate>
    </item>
    <item>
      <title>찜하기 구현 시 만난 에러 - NullPointerException</title>
      <link>https://clearing01.tistory.com/113</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPs6Pc/btrOY9aylr0/lGhiDZKVNP6XR4i1AIU9ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPs6Pc/btrOY9aylr0/lGhiDZKVNP6XR4i1AIU9ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPs6Pc/btrOY9aylr0/lGhiDZKVNP6XR4i1AIU9ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPs6Pc%2FbtrOY9aylr0%2FlGhiDZKVNP6XR4i1AIU9ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;735&quot; height=&quot;386&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mj1qW/btrO2pCK35U/fuhAmwHVe1hylq6Cw5HlBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mj1qW/btrO2pCK35U/fuhAmwHVe1hylq6Cw5HlBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mj1qW/btrO2pCK35U/fuhAmwHVe1hylq6Cw5HlBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmj1qW%2FbtrO2pCK35U%2FfuhAmwHVe1hylq6Cw5HlBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;379&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Java/Exception</category>
      <author>Clearing</author>
      <guid isPermaLink="true">https://clearing01.tistory.com/113</guid>
      <comments>https://clearing01.tistory.com/113#entry113comment</comments>
      <pubDate>Wed, 19 Oct 2022 08:52:10 +0900</pubDate>
    </item>
  </channel>
</rss>