<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>계란 블로그</title>
    <link>https://oranthy.tistory.com/</link>
    <description>Java Backend Developer
lan4250@naver.com
https://github.com/goraneee</description>
    <language>ko</language>
    <pubDate>Sun, 28 Jun 2026 22:27:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>계란 </managingEditor>
    <item>
      <title>Spring 로그인 실패 처리 롤백 문제 - TransactionTemplate로 해결</title>
      <link>https://oranthy.tistory.com/658</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  로그인 잠금(Lock) 처리 &amp;mdash; 트랜잭션 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 로그인할 때, (최초 로그인 실패 시점으로부터) 30분 내에 5회 이상 실패하면 계정에 잠금(lock)을 걸어서 로그인을 제한하는 기능을 구현했다. 그런데 이 과정에서 트랜잭션 때문에 실패 로그가 DB에 반영되지 않는 문제가 발생했다.&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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;❗ 문제 상황&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;login 메서드에서 회원이 비밀번호가 틀린 경우, 예외를 터뜨린다. 로그인 실패 로그를 DB에 저장하기 위해서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setLoginFailureHistory() 를 호출한다. 그러나...&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;❌ 예외가 발생하면서 부모 트랜잭션이 롤백 &amp;rarr;&lt;br /&gt;실패 기록(로그인 실패 카운트)이 DB에 커밋되지 않음&lt;/blockquote&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: #333333; text-align: left;&quot;&gt;즉&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;, 실패 로그를 남겨야하는데 예외가 터지면서 실패 로그 저장 로직이 롤백되버리는 문제가 발생했다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;592&quot; data-start=&quot;553&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;시도 1 &amp;mdash; REQUIRES_NEW 전파단계 적용 (실패)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 메서드와 실패 로그 저장 메서드에 둘다 트랜잭션 전파 단계를 requires_new 로 설정하여 실패 로그가 새로운 트랜잭션에서 저장되도록 만든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(requires_new:&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-end=&quot;739&quot; data-start=&quot;703&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;❌ 이유: 같은 클래스 내부 호출은 AOP가 적용되지 않음&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;834&quot; data-start=&quot;741&quot; data-ke-style=&quot;style2&quot;&gt;login 메서드가 같은 클래스의 setLoginFailureHistory()를 호출하면 setLoginFailureHistory 를 시작하는 순간, 프록시를 거치지 않기 때문에&amp;nbsp; @Transactional 자체가 적용되지 않는다.&lt;br /&gt;그래서 @Transactional(REQUIRES_NEW)는 무효.&lt;/blockquote&gt;
&lt;p data-end=&quot;875&quot; data-start=&quot;836&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1763738663876&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional(Transactional.TxType.REQUIRES_NEW)&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;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;553&quot; data-end=&quot;592&quot;&gt;&lt;b&gt;시도 2 &amp;mdash; TransactionTemplate (성공)&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;import org.springframework.transaction.support.TransactionTemplate;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;TransactionTemplate 을 사용하면 새로운 트랜잭션을 직접 시작할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transactionTemplate.execute() 메서드 내부는 항상 독립된 트랜잭션으로 실행된다. 따라서 실패 로그 저장을 예외 처리 부분과 분리하여 DB에 커밋 가능하다.&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;size18&quot;&gt;&lt;b&gt;⚠️ 주의점&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;TransactionTemplate를 쓰면 @Transactional 애터네이션은 제거해야한다. 같이 쓰면 트랜잭션이 이중으로 설정되므로 오류 가능성이 있기 때문이다.&amp;nbsp;&lt;/blockquote&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;size18&quot;&gt;&lt;b&gt;✔ 최종 동작 원리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;부모 메서드 login 에 트랜잭션 설정이 없음 &amp;rarr; 부모 트랜잭션 없음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;transactionTemplate.execute() 호출하는 순간 &amp;rarr; &lt;span style=&quot;text-align: left;&quot;&gt;새로운 트랜잭션이 시작된다.&amp;nbsp;&lt;/span&gt;&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;적용 코드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transactionTemplate 안에는 DB에 저장하는 부분만 넣고&amp;nbsp; throw 하는 부분은 그 다음에 넣어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 그 안에 throw 구문을 넣으면 하나의 트랜잭션 안에서 롤백이 일어나므로 실패로그는 저장 되지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1763742366909&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
public Map&amp;lt;String, Object&amp;gt; login(HttpServletRequest request, LoginRequestVO loginRequestVO) throws BadRequestException, AccessDeniedException {
    Map&amp;lt;String, Object&amp;gt; loginResult = new HashMap&amp;lt;&amp;gt;();
    /** email, password 검증, 잠김 여부 확인 **/
    /** 비밀번호 체크 */
    Boolean checkResult = BCrypt.checkpw(loginRequestVO.getPassword(), entity.getPassword());
    if (!checkResult) {
        transactionTemplate.execute(status -&amp;gt; {
            setLoginFailureHistory(entity);
            userRepository.save(entity);
            return null;
        });
        throw new AccessDeniedException(&quot;정보가 일치하지 않습니다. Password 를 확인하세요.&quot;);
    }
    return loginResult;
}

private void setLoginFailureHistory(UserEntity entity){
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime failedLoginDt = entity.getFailedLoginDt();    // 최초 fail 시점 (이 시점 기준으로 30분 동안 카운트)
    boolean isLockExpired
            = (failedLoginDt == null) || now.isAfter(failedLoginDt.plusMinutes(LOCK_WINDOW_MINUTES)); //  30분 지나면 로그인 실패 정보 reset

    /*** 로그인 최초 실패, 30분 경과한 경우 reset */
    if( entity.getLoginTryCount() == null || entity.getLoginTryCount() == 0 || isLockExpired == true){
        log.info(&quot;로그인 실패 정보를 초기화합니다.&quot;);
        entity.setLoginTryCount(1);
        entity.setFailedLoginDt(now);

    /*** 30분 내에 로그인 연속 실패  ***/
    } else if(isLockExpired == false){
        int curLoginTryCnt = entity.getLoginTryCount() + 1;
        entity.setLoginTryCount(curLoginTryCnt);
        if(curLoginTryCnt &amp;gt;= MAX_LOGIN_TRY_COUNT){
            log.info(&quot;로그인 실패 횟수 5회 이상입니다. 계정을 잠갔습니다.&quot;);
            entity.setLockYn(&quot;Y&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 일지/TroubleShooting</category>
      <category>java 로그인 비밀번호 계정 lock</category>
      <category>java 로그인 잠금 트랜잭션</category>
      <category>spring AOP transactional</category>
      <category>spring transactiontemplate</category>
      <category>TransactionTemplate</category>
      <category>스프링 트랜잭션</category>
      <category>회원 로그인 java</category>
      <category>회원 로그인 계정 잠금</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/658</guid>
      <comments>https://oranthy.tistory.com/658#entry658comment</comments>
      <pubDate>Sat, 22 Nov 2025 02:02:26 +0900</pubDate>
    </item>
    <item>
      <title>QueryDSL fetchJoin 사용 시 주의사항</title>
      <link>https://oranthy.tistory.com/653</link>
      <description>&lt;h2 data-end=&quot;135&quot; data-start=&quot;119&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;fetchJoin 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;235&quot; data-start=&quot;136&quot; data-ke-size=&quot;size16&quot;&gt;QueryDSL 에서의 fetchJoin은 &lt;b&gt;연관된 엔티티를 한 번의 쿼리로 즉시 로딩하고, 영속성 컨텍스트에 함께 적재하는 기능&lt;/b&gt;이다.&lt;br /&gt;즉, N+1 문제를 해결할 때 자주 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;344&quot; data-start=&quot;237&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;289&quot; data-start=&quot;237&quot;&gt;join : 단순히 SQL 조인만 수행 (Lazy 로딩될 수 있음)&lt;/li&gt;
&lt;li data-end=&quot;344&quot; data-start=&quot;290&quot;&gt;fetchJoin : 조인 + 즉시 로딩(Eager Loading), 영속성 컨텍스트에 연관 엔티티까지 채워 넣음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;489&quot; data-start=&quot;473&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;489&quot; data-start=&quot;473&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Eager 로딩과 fetchJoin의&amp;nbsp; 차이&lt;/b&gt;&lt;/h3&gt;
&lt;div style=&quot;background-color: #ffffff; color: #080808;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// Team 클래스
@OneToMany(fetch = FetchType.EAGER)
List&amp;lt;Member&amp;gt; memberList = new ArrayList&amp;lt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;576&quot; data-start=&quot;490&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;525&quot; data-start=&quot;490&quot;&gt;&lt;b&gt;Eager&lt;/b&gt;: 쿼리를 항상 즉시 로딩 &amp;rarr; 쿼리 제어 불가.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;525&quot; data-start=&quot;490&quot;&gt;fetch type = &quot;Eager_loading&quot; :&amp;nbsp; 항상 eager 로딩으로 고정한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;576&quot; data-start=&quot;526&quot;&gt;&lt;b&gt;fetchJoin&lt;/b&gt;: 필요할 때만 JPQL로 즉시 로딩 &amp;rarr; &lt;b&gt;쿼리 최적화&lt;/b&gt; 가능.&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 문제 상황 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;714&quot; data-start=&quot;592&quot; data-ke-size=&quot;size16&quot;&gt;Team &amp;harr; Member 관계에서 &lt;b&gt;N+1 문제&lt;/b&gt;가 발생했다.&lt;br /&gt;예를 들어 Team 목록을 조회하면, 각 Team의 members를 가져오기 위해 추가 쿼리가 팀 개수만큼 발생하는 상황이다.&lt;/p&gt;
&lt;p data-end=&quot;780&quot; data-start=&quot;716&quot; data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 fetchJoin을 적용했다. 이 과정에서 fetchjoin 을 적절하게 사용하기 위한 몇 가지 조건을 알게 되었다.&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;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주의 사항 &lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. FetchJoin 을 쓸 때 SELECT 에는 반드시 기준 테이블(엔티티 클래스)이 들어가야한다. (&lt;/b&gt;&lt;b&gt;DTO&lt;span&gt; 사용 불가능&lt;/span&gt;&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ex) 잘못된 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1755503829053&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query.select(team)
     .from(member)
     .leftJoin(member.team, team).fetchJoin();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1755496940886&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
org.hibernate.query.SemanticException: Query specified join fetching, but the owner of the fetched association was not present in the select list [SqmSingularJoin(kr.co.platform.mgmt_api.feature.entity.MemberEntity(MemberEntity).team(TeamEntity) : teamList)] at&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hibernate 6 - &lt;b&gt;SemanticException&lt;/b&gt; 오류 발생. 의미적으로 매끄럽지 않아서 오류 발생.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch의 주인은 반드시&lt;b&gt; select 절&lt;/b&gt;에 있어야한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;fetchJoin&lt;/span&gt; 쿼리에 엔티티가 아닌 다른 클래스를 넣는 경우에 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch는 반드시 그 연관 관계를 소유한 엔티티의 결과로 반환할 때만 허용되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetchJoin은 &lt;b&gt;영속성 컨텍스트&lt;/b&gt;에 엔티티를 넣기 위한 기능 &amp;rarr; 반드시 &lt;b&gt;엔티티 기준&lt;/b&gt;으로 select 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hibernate 6에서는 DTO projection과 같이 엔티티가 없는 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SemanticException&lt;/b&gt;이 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;1103&quot; data-end=&quot;1159&quot;&gt;  즉, fetchJoin을 사용할 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;반드시 엔티티 기준으로 쿼리를 작성&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;1103&quot; data-end=&quot;1159&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;1103&quot; data-end=&quot;1159&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;참고&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://discourse.hibernate.org/t/problems-with-join-fetch-using-criteria-api/10797?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://discourse.hibernate.org/t/problems-with-join-fetch-using-criteria-api/10797?utm_source=chatgpt.com&lt;/a&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;b&gt;Ex) 잘못된 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1755503876271&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query.select(Projections.constructor(MemberDTO.class, member.id, team.name))
     .from(member)
     .join(member.team, team).fetchJoin();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- fetchJoin() 의 결과를 projection 할 땐 반드시 Entity 클래스를 이용해서 받아와야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- DTO 클래스를 쓸 경우 오류발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- fetchJoin는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;엔티티&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반환 전용이기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO를 써야한다면 leftJoin() 으로 바꿔야한다. (&lt;s&gt;그럼 다시 n + 1 문제가 발생되므로 문제가 원점으로 돌아간다. &lt;/s&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 따라서 fetchJOin 써서 n + 1 문제 해결 후&amp;nbsp; code 조회하는 select 쿼리를 따로 날려서 둘을 조합했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO를 select 절에 두면 영속성 컨텍스트에 담을 엔티티가 없어 오류가 발생.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.&amp;nbsp; 단방향은 연관관계의 주인만 join의 기준 테이블이 될 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, Member 에는 @ManyToOne Team 에 대한 필드가 있고&amp;nbsp; Team 에는 Member&amp;nbsp; 정보가 없는 단방향 매핑의 경우를 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 Team 에 대한 Q 클래스인 QTeam 에는 Member 목록에 대한 필드가 없다.&amp;nbsp;&lt;br /&gt;따라서 연관 관계의 주인(FK 에 대한 정보를 가진 엔티티)을 중심으로 하여금 join 에 대한 기준 테이블이 될 수 있다는 뜻이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1755504852721&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
QMember member = QMember.member;
QTeam team = QTeam.team;

// Member &amp;rarr; Team (연관관계 주인인 Member 기준으로만 조인 가능)
List&amp;lt;Tuple&amp;gt; result = query
    .select(member, team)
    .from(member)
    .leftJoin(member.team, team) // ✅ 가능
    .fetch();
    
// Team &amp;rarr; Member (Team에는 Member 컬렉션이 없으므로 단방향 매핑에서는 불가
List&amp;lt;Tuple&amp;gt; result = query
    .select(member, team)
    .from(team)
    .leftJoin(team.members, member) // ❌ 경로 없음 &amp;rarr; 컴파일 에러&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 만약, 보조 테이블의 어떤 필드를 기준으로&lt;b&gt; Group By&lt;/b&gt; 하고 싶은 경우는 어떻게 해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보조 테이블의 칼럼으로 GROUP BY 시도하는 경우의 오류&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #080808;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;org.hibernate.query.PathException: Could not resolve join path 'memberEntity.team'
java.lang.IllegalArgumentException: org.hibernate.query.PathException: Could not resolve join path 'memberEntity.team'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Group by&amp;nbsp; 안에 department.team.member 를 넣으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;invalidPathException&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 발생하는 걸 확인할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;따라서, 기준 테이블로 모든 데이터를 가져온 다음, Java에서 원하는 데이터를 정제해야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 방식으로 데이터를 변환했다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1755506758615&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
1. 쿼리 실행 결과를 Map에 담아주기.
&amp;lt;hide/&amp;gt;
Map&amp;lt;Department, Map&amp;lt;Team, List&amp;lt;Member&amp;gt;&amp;gt;&amp;gt; result =
            memberList.stream()
                    .collect(Collectors.groupingBy(
                            t -&amp;gt; t.getTeam().getDepartment(), // 1차: Department 기준
                            Collectors.groupingBy(
                                    t -&amp;gt; t.getTeam(), // 2차: Team 기준
                                    Collectors.mapping(
                                            t -&amp;gt; t, // 3차: Member 기준
                                            Collectors.toList()
                                    )
                            )
                    ));

2.순환참조 오류 방지 -  &quot;일&quot; 쪽에서는  List 세팅 후, &quot;다&quot; 쪽에서는  null 처리
for(Map.Entry&amp;lt;Department, Map&amp;lt;Team, List&amp;lt;Member&amp;gt;&amp;gt;&amp;gt; entry : result.entrySet()) {
    Department department = entry.getKey();
    Map&amp;lt;Team, List&amp;lt;Member&amp;gt;&amp;gt; teamMap = entry.getValue();

    for (Map.Entry&amp;lt;Team, List&amp;lt;Member&amp;gt;&amp;gt; teamEntry : teamMap.entrySet()) {
        Team team = teamEntry.getKey();
        List&amp;lt;Member&amp;gt; memberList = teamEntry.getValue();
        for (Member member : memberList) {
            member.setTeam(null);
        }
        team.setMemberList(memberList);
        team.setDepartment(null);
    }
    List&amp;lt;Team&amp;gt; teamList = teamMap.entrySet().stream()
            .peek(e -&amp;gt; e.getKey().setMemberList(e.getValue()))
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    department.setTeamList(teamList);
}
- 양방향 참조를 막기 위한 null 세팅, list 값 세팅.

- null 세팅하지 않으면 순환 참조로 인한 문제가 발생한다.&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;&lt;/p&gt;
&lt;h2 data-end=&quot;1603&quot; data-start=&quot;1594&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실무적 대안&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1770&quot; data-start=&quot;1604&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1653&quot; data-start=&quot;1604&quot;&gt;fetchJoin으로 N+1 문제를 해결하고, 서비스 레이어에서 DTO 변환.&lt;/li&gt;
&lt;li data-end=&quot;1702&quot; data-start=&quot;1654&quot;&gt;단순 코드 값 같은 경우에는 &lt;b&gt;별도 쿼리 분리&lt;/b&gt; (예: 코드 테이블 조회).&lt;/li&gt;
&lt;li data-end=&quot;1770&quot; data-start=&quot;1703&quot;&gt;또는 @BatchSize, EntityGraph 등 Hibernate/JPA 옵션을 활용해 쿼리 최적화 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1782&quot; data-start=&quot;1777&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1941&quot; data-start=&quot;1783&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1816&quot; data-start=&quot;1783&quot;&gt;fetchJoin은 &lt;b&gt;엔티티 반환 전용&lt;/b&gt;이다.&lt;/li&gt;
&lt;li data-end=&quot;1849&quot; data-start=&quot;1817&quot;&gt;select 절에 DTO를 넣으면 오류가 발생한다.&lt;/li&gt;
&lt;li data-end=&quot;1941&quot; data-start=&quot;1850&quot;&gt;따라서 fetchJoin으로 연관 엔티티를 한 번에 가져온 뒤, 필요한 경우 DTO 변환을 따로 하거나 보조 쿼리로 조합하는 것이 가장 안전한 방법이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 일지</category>
      <category>fetchJoin</category>
      <category>fetchJoin SemanticException</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/653</guid>
      <comments>https://oranthy.tistory.com/653#entry653comment</comments>
      <pubDate>Mon, 18 Aug 2025 14:39:32 +0900</pubDate>
    </item>
    <item>
      <title>npm 과 pnpm 의 공통점/차이점</title>
      <link>https://oranthy.tistory.com/647</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Vue.js 를 빌드할 수 있는 도구에는&amp;nbsp; webpack, vite가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 회사에서 webpack을 써왔는데 이번에&amp;nbsp;Next.js (= React + 백엔드) 프로젝트를&amp;nbsp; Vite 기반의 Vue.js 로 리팩토링하는 중이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vite 라는 도구는 들어봤지만 사용해보는건 처음이라 관련된 명령어 npm, pnpm 공통점, 차이점에 대해 알아보려한다.&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.js란?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScrpit를 실행하기 위한 런타임 환경.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java로 따지면 JRE(Java Runtime Environment = JVM + 라이브러리, 자바 런타임 환경)&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;table style=&quot;border-collapse: collapse; width: 100%; height: 150px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;width: 7.6356%; height: 38px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 49.7287%; height: 38px;&quot;&gt;&lt;b&gt;npm &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%; height: 38px;&quot;&gt;&lt;b&gt;pnpm&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 7.6356%; height: 16px;&quot;&gt;방식&lt;/td&gt;
&lt;td style=&quot;width: 92.3643%; height: 16px;&quot; colspan=&quot;2&quot;&gt;Node.js 패키지 관리자. 명령어 대부분 동일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.6356%;&quot;&gt;사용&lt;/td&gt;
&lt;td style=&quot;width: 49.7287%;&quot;&gt;webpack 에서 사용&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #333333; text-align: start;&quot;&gt;vite 에서 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 7.6356%; height: 16px;&quot;&gt;효율성&lt;/td&gt;
&lt;td style=&quot;width: 49.7287%; height: 16px;&quot;&gt;비효율&lt;br /&gt;각 vue.js 프로젝트마다 node_modules 폴더가 있는데 이곳에 라이브러리 저장. 즉, 여러 프로젝트에 중복된 라이브러리가 저장될 수 있다. 디스크 공간을 낭비.&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%; height: 16px;&quot;&gt;효율적&lt;br /&gt;특정한 공유 저장소(전역 캐시)에 여러 Vue.js 프로젝트의 라이브러리가 모두 저장.&lt;br /&gt;각 프로젝트에서는 &lt;b&gt;심볼릭 링크&lt;/b&gt;만 연결하므로 중복이 없다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 7.6356%; height: 16px;&quot;&gt;속도&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.7287%; height: 16px;&quot;&gt;중복해서 다운 받기 때문에 느리다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%; height: 16px;&quot;&gt;캐시를 재사용하므로 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 7.6356%; height: 16px;&quot;&gt;호환성&lt;/td&gt;
&lt;td style=&quot;width: 49.7287%; height: 16px;&quot;&gt;안정적이며 가장 표준적이라 호환성이 높다.&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%; height: 16px;&quot;&gt;경로 문제(심볼릭 링크 구조 관련)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 7.6356%; height: 16px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 49.7287%; height: 16px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 42.6356%; height: 16px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 일지</category>
      <category>npm pnpm 차이</category>
      <category>webpack vite</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/647</guid>
      <comments>https://oranthy.tistory.com/647#entry647comment</comments>
      <pubDate>Sat, 17 May 2025 19:36:21 +0900</pubDate>
    </item>
    <item>
      <title>Message를 수신할 때마다 실행되는  MQTT Listener 만들기</title>
      <link>https://oranthy.tistory.com/644</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;MQTT&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;(Message Queue Telemetry Transfort)&lt;/span&gt;란?&lt;br /&gt;&lt;/span&gt;publish, subscribe 기반의 메시징 프로토콜이다. TCP/IP 프로토콜 위에서 동작하며 네트워크 대역폭이 제한되는 원격 위치와의&amp;nbsp; 연결을 위해서 설계되었다. 무선 네크워크에 연결된 단순한 기기들간의 간단한 소통을 위해 만들어진 만큼 리소스를 매우 적게 요구하므로 IoT(Internet of Things, 사물인터넷) 에서 가볍에 많이 쓰이는 프로토콜이다.&lt;br /&gt;&amp;nbsp;MQTT라는 프로토콜을 구현한 소프트웨어가 바로 메시지 브로커 mosquitto 이다. MQTT 를 깔고 나면 프로그램의 이름이 &quot;mosquitto&quot;인 것을 확인할 수 있다.&amp;nbsp;&lt;br /&gt;회사에 Message Queue 를 이용하는 Listener 서버가 있는데 이와&amp;nbsp; 관련된 부분을 구현하다가 이와 관련한 부분을 포스팅하려 한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;br /&gt;@KafkaListener&lt;span&gt; 는&lt;/span&gt;&lt;/span&gt; kafka 서버에서 특정 토픽에 대한 메시지를 받을 때마다 어떤 메서드를 실행하도록 설정하는 애너테이션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;ActiveMQ도 마찬가지로&amp;nbsp; @JmsListener 를 붙여서 ActiveMQ 서버에서 메시지를 수신할 때마다 실행되는 메서드에 적용할 수 있다. 회사에서 Message Broker를 세 종류 (Kafka, ActiveMQ, MQTT) 를 사용 중인데 yml 파일에서 어떤 Message Queue 를 쓸 것인지 정하면 이 에 따라서, 셋 중 하나가 실행되도록 설정하려한다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. @KafkaListener&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2. &lt;/span&gt;@JmsListener&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. (MQTT subscribe 하는 메서드)&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;letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #080808; letter-spacing: 0px;&quot;&gt;그런데 MQTT 에서는 다른 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Message Queue&lt;span&gt; 와 다르게 자동으로 Listener 설정하는 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #080808; letter-spacing: 0px;&quot;&gt;애너테이션이 없어서 리스너 역할을 하는 메서드를 직접 만들려고 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #080808;&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dependency 추가 (&lt;span style=&quot;background-color: #ffffff; color: #080808; text-align: start;&quot;&gt;Java 8&lt;/span&gt;&amp;nbsp; 기준)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;org.eclipse.paho.client.mqttv3: 순수 MQTT Client 라이브러리. low level로 직접 publish / subscribe 가능.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 라이브러리만 쓴다면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;MQTT 콜백 메서드를 써서 messageArrived() 메서드를 오버라이드하면 subscribe 기능을 구현할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;spring-integration-mqtt: Spring Framework 의 @ServiceActivator를 이용해서 MQTT 서버에서 특정 Topic에 대한 메시지를 &lt;span style=&quot;background-color: #ffffff; color: #080808; text-align: left;&quot;&gt;subscribe &lt;/span&gt;할 때마다 특정 메서드가 실행되도록 설정한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동으로 publish / subscribe&amp;nbsp; 설정 가능.&amp;nbsp; MQTT 를 추상화해서 쉽게 다룰 수 있다. (Channel, Adaptor 를 제공.)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1745986504748&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.eclipse.paho&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;org.eclipse.paho.client.mqttv3&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.2.5&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.integration&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-integration-mqtt&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;5.5.15&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Config 클래스&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mqttClientFactory: MQTT broker 와 연결하기 위한 클라이언트를 생성하고 접속 정보를 설정한다. 이 때&amp;nbsp; &quot;tcp&quot;프로토콜 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inbound:&amp;nbsp; MQTT broker로부터 메시지를 받아오는 Adaptor 역할이다. 이걸&amp;nbsp;mqttInputChannel 로 전달한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mqttInputChannel: MQTT 메시지가 전달될 통로 이름이다. 메시지가 이 채널을 통해서 흐른다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745979221963&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
@Configuration
@RequiredArgsConstructor
public class MqttListenerConfig {

    @Value(&quot;${spring.mqtt.server}&quot;)
    private String MQTT_SERVER;

    @Value(&quot;${spring.mqtt.qos}&quot;)
    private String MQTT_QOS;

    private final ApplicationProperty property;

    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }

    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        MqttConnectOptions options = new MqttConnectOptions();
        options.setServerURIs(new String[]{MQTT_SERVER});
//        options.setUserName(&quot;&quot;);
//        options.setPassword(&quot;&quot;.toCharArray());
        factory.setConnectionOptions(options);
        return factory;
    }

    @Bean
    public MqttPahoMessageDrivenChannelAdapter inbound() {
        String clientId = &quot;MQTTListener-&quot; + UUID.randomUUID();
        String topic = property.getQueueName();
        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(clientId, mqttClientFactory(), topic);
        adapter.setOutputChannel(mqttInputChannel());
        adapter.setQos(MQTT_QOS != null ? Integer.valueOf(MQTT_QOS) : 0);
        return adapter;
    }
}&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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Service 클래스&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;어떤 메서드에 @ServiceActivator를 붙이면&lt;/span&gt;&amp;nbsp; 앞에서 살펴본 mqttInputChannel 에서 데이터를 받을 때마다 해당 메서드를 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의) 사용 중인 middleware 가&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;MQTT&lt;span&gt; &lt;/span&gt;&lt;/span&gt;가 아닌 경우(yml 파일에 middleware 종류에 대한 설정이 있음)에도 MQTT 서버에서 메시지를 받으면 이 메서드는 반드시 실행된다. 이 때 실행되지 않도록 반드시 조건문을 다음과 같이 넣어줘야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(@&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;JmsListener&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;KafkaListener&lt;span&gt; 도 마찬가지이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;yml 파일에 middleware로 설정한 값에 따라서 특정 Message Queue 만 동작하도록@&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;ConditionalOnProperty를 적용하려했으나 아예 작동이 안되서 다음과 같은 if 문으로 return 하도록 설정했다. &lt;/span&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1745987413948&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
@ServiceActivator(inputChannel = &quot;mqttInputChannel&quot;)
public void handleMqttMessage(String message) {
    if(!&quot;mqtt&quot;.equals(queue.getMiddleware())) {
        return;
    }
    // 받은 메시지를 처리한다. 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 일지</category>
      <category>java mqtt</category>
      <category>spring mqtt</category>
      <category>spring mqtt listner</category>
      <category>spring mqtt subscriber</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/644</guid>
      <comments>https://oranthy.tistory.com/644#entry644comment</comments>
      <pubDate>Wed, 30 Apr 2025 14:10:32 +0900</pubDate>
    </item>
    <item>
      <title>맥북 pro 12일 사용 후기 (M4 pro  + Nano texture)</title>
      <link>https://oranthy.tistory.com/639</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI3b6a/btsMSRQjIYc/7fOGTClhNdjstyZzHyc791/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI3b6a/btsMSRQjIYc/7fOGTClhNdjstyZzHyc791/img.png&quot; data-alt=&quot;맥북 환불 3시간 전 ^^&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI3b6a/btsMSRQjIYc/7fOGTClhNdjstyZzHyc791/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI3b6a%2FbtsMSRQjIYc%2F7fOGTClhNdjstyZzHyc791%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;389&quot; height=&quot;389&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;맥북 환불 3시간 전 ^^&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SxNIR/btsMThnkyvw/anKBus7v4lqEbfXKq2qKaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SxNIR/btsMThnkyvw/anKBus7v4lqEbfXKq2qKaK/img.png&quot; data-alt=&quot;맥북 pro M4 48GB 1TB Nano texture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SxNIR/btsMThnkyvw/anKBus7v4lqEbfXKq2qKaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSxNIR%2FbtsMThnkyvw%2FanKBus7v4lqEbfXKq2qKaK%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;637&quot; height=&quot;580&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;맥북 pro M4 48GB 1TB Nano texture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;구매한 맥북 사양&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777;&quot;&gt;웹 개발을 위해 구매한 맥북 사양은 다음과 같다.&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;M4&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #777777;&quot;&gt;pro 칩&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;RAM 48GB &lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;SSD 1TB &lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;14코어 CPU. 20코어 GPU&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #777777;&quot;&gt;+ Nano texture(나노 텍스처) : 밝은 곳에서 빛이 비치더라도 선명하게 볼 수 있는 기능 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #777777;&quot;&gt;+ Apple care plus&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 후기 작성 배경&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맥북을 열흘 정도 사용해보고 내 맥북에서 발생한 문제와 장단점, 사용 후기를 쓰려 한다.&amp;nbsp; Apple care plus 으로 환불 가능한 기간이 2주인데 열흘 동안 문제가 지속돼서 바로 환불했다. 맥북을 며칠 사용해보니 키보드, 마우스 이슈가 있었다.&lt;br /&gt;온라인 스토어에서 주문하고 애플 스토어에 직접 방문해서 반품하고 왔다. (배송으로 환불 신청해도 되지만 직접 가는 게 환불 처리가 빠름)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;내 맥북에서 발생한 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 한영 변환 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chrome 에서 영어는 문제없이 입력되는데 &lt;b&gt;한글 모드&lt;/b&gt;로 바꾸면 커서가 깜빡일 뿐 &lt;b&gt;입력이 안된다&lt;/b&gt;. (작업 표시줄에서 '한' 표시가 나오지만 입력이 안된다. 적어도 몇 분 이상 지속..) 이 현상이 하루에 몇 번이고 반복됐다.&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;gt; 결국 karabiner (Mac에서 키보드 매핑을 바꿔주는 프로그램) 를 지웠다. 그러고나서 Caps Lock 을 한영 변환 키로 사용.&lt;br /&gt;&amp;nbsp; -&amp;gt; &lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;karabiner&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;&amp;nbsp;프로그램을 제거&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;했는데도&lt;/span&gt;&amp;nbsp;&lt;b&gt;같은 문제(버벅임)가 지속적으로 발생했다.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&amp;gt; 특히, &lt;b&gt;Chrome&lt;/b&gt; 에서 입력 문제가 반복됐고 IDE(IntelliJ), 카카오톡에서는 한글 입력에 문제가 없어서 이걸 복붙하는 방식으로 한글을 입력했다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 마우스 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 좌클릭이 아예 안 먹힐 때가 있었다. 우클릭, 드래그는 정상이었으나 좌클릭만 안됐다.&amp;nbsp;&lt;br /&gt;&amp;gt; 그치만 이 문제는 열흘 중 하루 정도(?)만 발생한 문제이다.&amp;nbsp;&lt;br /&gt;&amp;gt; 마우스 (로지텍 블루투스 연결 상태)&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 단축키 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 윈도우와 단축키가 다름.&lt;br /&gt;&amp;gt; 이런 부분은 시간 지나면 익숙해져서 해결될거라 생각함. 1번 문제가 심각해서 반품하지 않을 수 없었다.&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 애플 채팅 상담 결과&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;급한대로 온라인으로 애플 채팅 상담을 신청했다. &lt;span style=&quot;color: #333333;&quot;&gt;채팅 상담 직원이 &lt;/span&gt;알려준대로 두 가지 방법으로 테스트했다.&amp;nbsp;&lt;br /&gt;&amp;gt; 진단 테스트를 실행해서 문제의 참조 코드를 확인한다.&amp;nbsp;&lt;br /&gt;&amp;gt; &lt;a href=&quot;https://support.apple.com/ko-kr/116946&quot; target=&quot;_self&quot;&gt;&lt;span&gt;안전 모드&lt;/span&gt;&lt;/a&gt;로 부팅해서 같은 문제가 나오는지 확인&lt;br /&gt;(&amp;nbsp; + 평일, 주말 관련 없이 저녁까지 채팅 상담이 가능함.)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 애플 진단 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;1166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxsFMX/btsMSQX8PuG/3KYO95RnoKSA0tXb8rusR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxsFMX/btsMSQX8PuG/3KYO95RnoKSA0tXb8rusR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxsFMX/btsMSQX8PuG/3KYO95RnoKSA0tXb8rusR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxsFMX%2FbtsMSQX8PuG%2F3KYO95RnoKSA0tXb8rusR0%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;556&quot; height=&quot;391&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;1166&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;1400&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blGejn/btsMTLBAykd/j1LE5E5UR8sZDcKFfbbES0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blGejn/btsMTLBAykd/j1LE5E5UR8sZDcKFfbbES0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blGejn/btsMTLBAykd/j1LE5E5UR8sZDcKFfbbES0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblGejn%2FbtsMTLBAykd%2Fj1LE5E5UR8sZDcKFfbbES0%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;551&quot; height=&quot;276&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;결과: ADP000 (하드웨어는 아무 문제 없다. )&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. &lt;/b&gt;&lt;b&gt;안전 모드로 부팅 후 확인&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안전 모드로 부팅하면 작업 표시줄에 빨간 글씨로 안전 모드가 표시된다. 여전히 키보드 문제가 발생했다.&amp;nbsp;&lt;br /&gt;결론: 소프트웨어 문제로 보인다. 그래서 결국 반품 결정.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;직접 애플 스토어에 가서 반품 하기 위한 준비 사항&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 주문번호: W____ (뒷 부분은 숫자)&lt;br /&gt;&amp;gt; PC 비밀 번호&lt;br /&gt;&amp;gt; 결제한 카드는 필요 없음.&lt;br /&gt;&amp;gt; 갈색 박스 없이 내부 흰색 포장 박스만 그대로 포장해서 들고 가면 된다.&amp;nbsp;&lt;br /&gt;&amp;gt; 신학기 프로모션으로 구매한 에어팟이 있다면 모두 반환해야 한다.&amp;nbsp;&lt;br /&gt;&amp;gt; 환불금은 결제한 카드로 2-3일 내에 들어온다.&amp;nbsp;&lt;br /&gt;&amp;gt; 공장 초기화를 하고 가면 조금 빨리 끝남.&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;맥북 장단점과 결론&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한글 입력 문제가 커서 그렇지 다른 면에서는 거의 만족스러웠다. 그래서 다시 한번 주문하려한다. (새로운 맥북으로는&amp;nbsp;karabiner 같은 도구 없이 기본 설정 그대로 써보려 함.)&amp;nbsp;그래도 같은 문제(간헐적으로 한글이 몇 분 동안 입력되지 않는 문제)가 발생한다면 포기하고 window 로 돌아갈 생각이다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;맥북 장점&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2152&quot; data-origin-height=&quot;1432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwLoCB/btsMTjMe0Ux/7sKjkUPgNKQXqMapnqTmK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwLoCB/btsMTjMe0Ux/7sKjkUPgNKQXqMapnqTmK0/img.png&quot; data-alt=&quot;나노 텍스처 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwLoCB/btsMTjMe0Ux/7sKjkUPgNKQXqMapnqTmK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwLoCB%2FbtsMTjMe0Ux%2F7sKjkUPgNKQXqMapnqTmK0%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;2152&quot; height=&quot;1432&quot; data-origin-width=&quot;2152&quot; data-origin-height=&quot;1432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나노 텍스처 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzO88r/btsMUyhj0yE/9jUn0eFyiqpiK1mwVVGEd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzO88r/btsMUyhj0yE/9jUn0eFyiqpiK1mwVVGEd0/img.jpg&quot; data-alt=&quot;나노 텍스처 - 노트북을 기울여서 훨씬 선명함&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzO88r/btsMUyhj0yE/9jUn0eFyiqpiK1mwVVGEd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzO88r%2FbtsMUyhj0yE%2F9jUn0eFyiqpiK1mwVVGEd0%2Fimg.jpg&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;1440&quot; height=&quot;1080&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나노 텍스처 - 노트북을 기울여서 훨씬 선명함&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예쁨. 반짝반짝 영롱하고 깔끔. 화질 좋음.&lt;br /&gt;mac 터미널로 원격에 접속하기 편하다.&amp;nbsp;&lt;br /&gt;터미널에 접속할 때도 putty 같은 걸 깔지 않아도 .key 파일만 있으면 SSH 프로토콜로 접속할 수 있어서 좋았다.&amp;nbsp;&lt;br /&gt;그리고 Linux과 명령어와 폴더 구조가 같아서 &lt;span style=&quot;color: #333333;&quot;&gt;웹 개발에 있어서&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;이런 부분은큰 장점이라고 느꼈다.&amp;nbsp;&lt;br /&gt;Nano texture(나노 텍스처) 기능: 20 만원 정도 추가하면 되는데 야외 작업하거나 밝은 곳에서 사용하는 사람들에게 적합하다. 밝은 카페에서 써보니까 확실히 잘 보여서 편했다. (그런데 필수는 아니라고 생각함.)&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;맥북&lt;/b&gt;&lt;b&gt; 단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키보드 매핑 이슈 (다른 Mac 유저 얘기를 들어봐도 한영 변환 시 시간 문제(몇 초동안)는 흔하다고 한다.&amp;nbsp;&lt;br /&gt;가격이 비쌈.&lt;br /&gt;무겁다. 16인치 pro 기준 2kg 넘는다.&lt;br /&gt;내장 키보드에는 Del 키가 없다. (fn 키를 쓰면 Del 기능을 쓸 수는 있음.)&lt;br /&gt;한무무를 연결했는데 한무무 스페이스 바의 오른쪽에 있는 기능 키들은 거의 쓸 수 었었다.&amp;nbsp;&amp;nbsp;(이 부분은 쓸 수 있는건지 아닌지 정확히 모름)&lt;/p&gt;</description>
      <category>m4 karabiner</category>
      <category>mac karabiner</category>
      <category>mac karabiner 오류</category>
      <category>mac 장단점</category>
      <category>mac 키보드 매핑</category>
      <category>mac 하드웨어 테스트 진단</category>
      <category>nano texture 나노 텍스처</category>
      <category>맥북 karabiner 호환성 문제</category>
      <category>맥북 환불</category>
      <category>애플 스토어 직접 환불</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/639</guid>
      <comments>https://oranthy.tistory.com/639#entry639comment</comments>
      <pubDate>Sun, 23 Mar 2025 20:55:59 +0900</pubDate>
    </item>
    <item>
      <title>Spring  bean life cycle  - Chrome driver 충돌 문제</title>
      <link>https://oranthy.tistory.com/634</link>
      <description>&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;&lt;br /&gt;개인 프로젝트에서 Chrome driver 를 사용하는데 Linux 버전에 대한 Chrome 옵션 설정을 추가하자마자&lt;span&gt;&amp;nbsp;&lt;/span&gt;Spring Bean 생성 오류가 나면서 서버가 다운됐다.&amp;nbsp;Chrome Driver - Spring 생명 주기 충돌 문제에 관한 포스팅을 하려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Window의 경우에는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Chrome&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 옵션을 넣을 필요가 없으나 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;OS가 Linux 인 경우에는 Chrome 옵션(headerless 등)을 넣어야한다.&lt;s&gt; (docker 에서는 linux 기반으로 돌리는데&amp;nbsp; linux에서는 chrome&amp;nbsp; option 값이 필요함.)&lt;/s&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-start=&quot;0&quot; data-end=&quot;43&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스프링 빈 생성 주기 (Spring Bean Lifecycle)&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot; data-start=&quot;44&quot; data-end=&quot;309&quot;&gt;
&lt;li data-start=&quot;44&quot; data-end=&quot;105&quot;&gt;&lt;b&gt;객체 생성&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; new를 통해 빈 객체가 생성됨. (&lt;b&gt;@Component, @Bean&lt;/b&gt; 등)&lt;/li&gt;
&lt;li data-start=&quot;106&quot; data-end=&quot;154&quot;&gt;&lt;b&gt;의존성 주입&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; @Autowired 등을 통해 필요한 의존성이 주입됨.&lt;/li&gt;
&lt;li data-start=&quot;155&quot; data-end=&quot;214&quot;&gt;&lt;b&gt;초기화 (InitializingBean, @PostConstruct)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; 초기 설정 수행.&lt;/li&gt;
&lt;li data-start=&quot;215&quot; data-end=&quot;243&quot;&gt;&lt;b&gt;사용&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; 빈이 애플리케이션에서 사용됨.&lt;/li&gt;
&lt;li data-start=&quot;244&quot; data-end=&quot;309&quot;&gt;&lt;b&gt;소멸 (DisposableBean, @PreDestroy)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; 애플리케이션 종료 시 정리 작업 수행.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Config 클래스의 생성자&amp;nbsp; 안에서 &lt;br /&gt;- Window : new ChromeDriver() / &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Linux: new &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;ChromeDriver(options)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 옵션을 줘서 전역 변수 driver 인스턴스를 생성했는데 spring 서버가 시작되지 않고 완전 다운됐다.&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;Ex) 에러 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service 클래스의 생성자 안에서 driver&amp;nbsp; 인스턴스 할당&lt;/p&gt;
&lt;pre id=&quot;code_1741511904559&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;

    private WebDriver webDriver;

    @Value(&quot;${spring.profiles.active}&quot;)
    private String profile;

    @PostConstruct
    void init() {
        ChromeOptions options = new ChromeOptions();
        System.setProperty(&quot;webdriver.chrome.driver&quot;, chromeDriverPath);
        if (profile.equals(&quot;dev&quot;)) {  // 리눅스 배포용 세팅
            options.addArguments(&quot;--headless&quot;);
            options.addArguments(&quot;--no-sandbox&quot;);
            options.addArguments(&quot;--disable-dev-shm-usage&quot;);
            log.info(&quot;Linux chrome 옵션 세팅 완료.&quot;);
            webDriver = new ChromeDriver(options);
        } else {
            webDriver = new ChromeDriver();
        }
    }&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;Note) 결과&lt;/p&gt;
&lt;pre id=&quot;code_1741513520538&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'checkInController' defined in file [C:\Users\Ran\IdeaProjects\attendance-management\build\classes\java\main\com\automation\attendancemanagement\controller\CheckInController.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'checkInService': Invocation of init method failed

Caused by: org.openqa.selenium.SessionNotCreatedException: Could not start a new session. Possible causes are invalid address of the remote server or browser start-up failure. 

Command: [null, newSession {capabilities=[Capabilities {browserName: chrome, goog:chromeOptions: {args: [], extensions: []}}]}]

Caused by: java.io.UncheckedIOException: java.io.IOException: Cannot run program &quot;C:\Users\Ran\Downloads\chromedriver-win64\chromedriver-win64&quot;: CreateProcess error=5, 액세스가 거부되었습니다
	
Caused by: java.io.IOException: Cannot run program &quot;C:\Users\Ran\Downloads\chromedriver-win64\chromedriver-win64&quot;: CreateProcess error=5, 액세스가 거부되었습니다
	
Caused by: java.io.IOException: CreateProcess error=5, 액세스가 거부되었습니다&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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;원인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 새로운 크롬 드라이버 인스턴스를 생성하면 스프링 컨테이너가 관리하는 빈으로 등록되지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( @Bean, @Component 로 등록하지 않았기 때문에 당연한 결과이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 @Bean 을 통해서 메서드 안에서 WebDriver를 빈으로 등록하는 과정이 필요하다.&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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChromeDriver 를 bean 으로 등록하는 과정이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;profiles 에 따라서 두 가지 bean 중 하나만 활성화되도록 결정한다.&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;&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;Ex)&amp;nbsp; 정상 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- @Bean을 등록함에 따라서Spring 컨테이너가 Web driver 를 관리하도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- profile 값에 따라서 둘 중 하나의 bean만 생성하도록 설정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741513299524&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
@Slf4j
@Configuration
public class WebDriverConfig {
/*
    @Bean
    @Profile(&quot;local&quot;)
    public WebDriver windowwebDriver() {
        System.out.println(&quot; 2. 빈생성 완료 - window&quot;);
        return new ChromeDriver();
    }

    @Bean
    @Profile(&quot;dev&quot;)
    public WebDriver linuxWebDriver() {
        ChromeOptions options = new ChromeOptions();
        options.addArguments(&quot;--headless&quot;);
        options.addArguments(&quot;--no-sandbox&quot;);
        options.addArguments(&quot;--disable-dev-shm-usage&quot;); // 리소스 부족 방지
        System.out.println(&quot; 2. 빈생성 완료 - linux&quot;);
        return new ChromeDriver(options);
    }*/&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;&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;</description>
      <category>BeanCreationException</category>
      <category>beaninstantiationexception</category>
      <category>illegalstateexception</category>
      <category>nosuchdriverexception</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/634</guid>
      <comments>https://oranthy.tistory.com/634#entry634comment</comments>
      <pubDate>Sat, 8 Mar 2025 02:52:13 +0900</pubDate>
    </item>
    <item>
      <title>18회 IoT 지식능력검정 후기(2024 12 01) 및 오답 정리</title>
      <link>https://oranthy.tistory.com/628</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2024/12/01&amp;nbsp; 18회&amp;nbsp; IoT 지식능력검정 &lt;/b&gt;&lt;b&gt;필기 후기&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;회사에서 IoT 관련된 일을 하던 중 권유를 받아 시험에 응시하게 되었다. 평소에 네트워크 지식이나 IoT 관련 지식이 부족하다고 느껴서 공부하기에 좋은 기회라는 생각이 들었다. 그래서 컴퓨터 과학 지식도 넓힐 겸 한 달 전부터 공부를 시작했다. 합격만을 목표로 한다면 한달보다 짧은 기간(2-3주)에도 가능할 거라 본다.&lt;br /&gt;책은 사지 않고 9회분 기출 문제를 하나씩 풀면서 notion 에 정리했다. CBT IOT 기출문제 &lt;a href=&quot;https://www.comcbt.com/xe/cco&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고&lt;/a&gt; &amp;nbsp;&lt;br /&gt;기출을 풀어보면 알겠지만 정처기 필기처럼 문제 은행식이고 정답 번호까지 동일한 문제가 다수였다. 책은 사지 않아도 충분히 합격 가능한 시험이다. (교재를 찾아봐도 가장 최근에 나온 책이 2019년에 나온 책일 정도)&lt;br /&gt;오늘 시험 본 결과 기출 문제의 내용에서 벗어나는 문제는 대략 3 개(총 50 문제) 미만이었던 걸로 기억한다. 나머지는 대부분 기출에 나왔던 문제이거나 기출을 공부하며 이해했다면 확실히 풀 수 있는 문제들이었다.&amp;nbsp;&lt;br /&gt;참고)&lt;br /&gt;10:00 - 11:20 시험이었으며 40분 부터는 퇴실이 가능해서 40분이 되자마자 모두들 우르르 교실을 나갔다.&amp;nbsp;&lt;br /&gt;고사장(철도고등학교)&amp;nbsp; 3층에는 일본어 시험이 있었고 6층에서는 IoT / RFID 응시자가 함께 같은 고사장에서 응시했다.&amp;nbsp;&lt;br /&gt;시험 장소는 단 두 곳 용산철도고/ 화천정보산업고 둘 중에서만 선택 가능했다.&lt;/blockquote&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오답 &lt;/b&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;&lt;b&gt;note&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;amp;cube 코어블록 종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;mobius : onem2m 앱 검색 가능 (보기: everything, thing+)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;(신) Adhoc모드. WIFI AP 를 통해서만 디바이스끼리 통신 가능한가?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;&amp;nbsp; - adhov 모드에서. 두 개의 디바이스가 wifi를 통해 통신하려면 적어도 하나는 wifi ap 이어야한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;LoRa ,LTE-M, NB-IoT 비교&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;사물인터넷 디바이스 소프트웨어 플랫폼&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;비즈니스 모델 캔버스 canvas&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;&lt;b&gt;기출 note&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;빅데이터 4요소는 3요소에&amp;nbsp; &quot;VR 증강현실&quot; 을 더한 것이다. (x)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;zigbee&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;z-wave&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;IEEE&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;IETF&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;CoAP&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot; data-token-index=&quot;0&quot;&gt;AllSeen alliance&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;AllJoyn&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;xmpp&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;mqtt&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;비정형 데이터&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;MEMS&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;오픈소스 하드웨어(OSHW) 결과물&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;IEEE 802.11ac&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot; data-token-index=&quot;0&quot;&gt;6LoWPAN &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;tissue 모델&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;혁신성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;연관 규칙 학습&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;mbed OS&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot; data-token-index=&quot;0&quot;&gt;비글본 블랙&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;아두이노:&amp;nbsp; ....~ 생태계를 만들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;oneM2m 기본 계층 모델&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;사물인터넷 플랫폼 기술 1) 장치 관리 기술 OMA DM&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;사물인터넷 플랫폼 기술 3) 검색 기술&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;시맨틱&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;블루투스 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;혁신성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot;&gt;RADAR&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffffff;&quot; data-token-index=&quot;0&quot;&gt;비인가 접근&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자격증</category>
      <category>iot 18회</category>
      <category>iot 시험</category>
      <category>iot 시험 후기</category>
      <category>IoT 지식능력검정</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/628</guid>
      <comments>https://oranthy.tistory.com/628#entry628comment</comments>
      <pubDate>Sun, 1 Dec 2024 20:43:59 +0900</pubDate>
    </item>
    <item>
      <title>2024 3회 정보처리기사  필기 후기 및 오답정리</title>
      <link>https://oranthy.tistory.com/622</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2024 3회 정보처리기사&amp;nbsp; 필기 후기&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;7월 7일에 응시한&amp;nbsp; 3회 정처기 시험 후기를 남기려한다.&lt;br /&gt;그동안 필기는 요약본, 기출문제만으로 된다는 합격 후기를 많이 봤다. 그래서 두 가지로만 3주 정도(직장인 기준) 공부하고 합격했다. 막상 시험을 보니 시나공 요약본 2023, 기출문제 (2020-2022)에서 본 적 없는 문제가 체감 상 5 ~ 10문제는 되는것 같았다 ㅠㅠ 90점 이상 고득점을 바란다면 이것만으로는 부족하지만 합격 커트라인 60점을 넘기 위해서는 괜찮은 편이다.&amp;nbsp;&lt;br /&gt;최신 기출 문제집에는 2023년 기출 문제들을 확인할 수 있다고 하니 참고하면 도움이 될 것 같다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;시험 시간 2시간 반 동안에 100 문제를 풀면 되는데 넉넉해서 중간에 나왔다. 다른 기사 시험에 응시하는 분들도 섞여있었고 많은 사람들이 중도 퇴실했다. 3번 정도 검토하고 나왔다. 정처기는 시험끝나자마자 점수가 나와서 합불 여부를 알 수 있어서 좋았다.&lt;br /&gt;&lt;br /&gt;참고)&amp;nbsp;&lt;br /&gt;유의할 점이 있다면 필기 응시 후 실기를 접수하기 전에 산업인력공단에 서류(졸업증명서, 경력증명서 등)를 제출해야하는데 따로 문자나 이메일로 알려주지 않으니 기간에 맞춰 알아서 보내야 한다.&lt;br /&gt;강남의 솔데스크에서 응시했다. 시험 보는 교실 내부는 괜찮았지만 대기 장소가 협소했다. 라운지 같은 곳에 테이블 몇 개 있었는데 인원이 많다보니 대부분은 더운 곳에서 서서 기다렸다 ㅠㅠ 그래서 같은 곳에서 응시한다면 아주 일찍 가는 건 추천하지 않는다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소프트웨어 설계&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나선형 모델&lt;/li&gt;
&lt;li&gt;스크럼,&amp;nbsp; 스프린트&lt;/li&gt;
&lt;li&gt;Man month&lt;/li&gt;
&lt;li&gt;정적 다이어그램 - 클래스 다이어그램&lt;/li&gt;
&lt;li&gt;UML&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임계 경로 Critical path method&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최장 경로 의미&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 패턴이 아닌것은?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵저버 패턴 - 행위 패턴이라 고름&lt;/li&gt;
&lt;li&gt;프록시 패턴은 생성 패턴인 줄 알았는데 구조 패턴이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소프트웨어 개발&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하향식 테스트-&amp;nbsp;&amp;nbsp;stub&lt;/li&gt;
&lt;li&gt;테스트 오라클(Tets Oracle)&lt;/li&gt;
&lt;li&gt;캡슐화의 장점&lt;/li&gt;
&lt;li&gt;객체지향 장점이 아닌것은? 재사용 어렵다&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버블 소트일 때 최악의 경우 복잡도&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최선 : O(n)&lt;/li&gt;
&lt;li&gt;평균 : O(n^2)&lt;/li&gt;
&lt;li&gt;최악 : O(n^2)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;삽입 정렬 1회전 후 결과&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분할 정복&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;?? 의 목표는 분할 정복이다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해싱 함수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스머프: 32BIT에서 구현을 용리하게 할 목적으로 개발.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;블록 크기: 512bit, 키길이에 따라 128, 256으로 분류.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 교체 알고리즘&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LRU(Least Recently Used): 현재를 기준으로 가장 오랫동안 참조되지 않은 페이지 자리에 새로운 데이터를 넣는다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터무결성 검사도구&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tripwire: 시스템 파일과 설정의 무결성을 검사해서 비정상적인 변경을 탐지하는 보안 도구이다.&amp;nbsp;Cracker 가 침입했을 때 감지한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Cracker(크래커): 컴퓨터 시스템에 불법적으로 접근해서 데이터를 훔치거나 시스템을 파괴하는 악의적인 해커&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 프레임워크&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XUnit&lt;/li&gt;
&lt;li&gt;STAF&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;GIT&amp;nbsp; 소프트웨어의 버전 등록 관련 주요 기능&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;import&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Brooks' Law(브룩스 법칙): 프로젝트 기간의 마지막에 한 명이 더 투입되면 오히려 마감이 늦어지는 것&lt;/li&gt;
&lt;li&gt;Boehm's Law(베어의 법칙): 결함을 초기에 발견할수록 비용이 적게든다.&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터베이스&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View&lt;/li&gt;
&lt;li&gt;DML&lt;/li&gt;
&lt;li&gt;시스템 카탈로그&lt;/li&gt;
&lt;li&gt;UNION ALL 결과&lt;/li&gt;
&lt;li&gt;분산 저장소&lt;/li&gt;
&lt;li&gt;무결성 종류: 개체 무결성 NULL, 중복값&lt;/li&gt;
&lt;li&gt;관계 연산자 표기법: JOIN 나비 모양&amp;nbsp;&lt;/li&gt;
&lt;li&gt;트랜잭션 인터페이스 설계는 DB 설계 단계 중에서 언제 이뤄지는가?&lt;/li&gt;
&lt;li&gt;개념적 설계/ 논리적 설계/ 트랜잭션 인터페이스 설계&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 상태&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;partially commit: 연산 끝난 후 커밋 직전의 상태.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CASCADE 설정 후 부모테이블의 row 삭제 결과&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모두 삭제 된다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 클러스터&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비슷한 데이터/ 끼리 함꼐 호출되는 경우가 많아서 메모리상에서 물리적으로 가깝게 저장한다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BCNF가 되기 위한 조건은?&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결정자이면서 후보키가 아닌 것 제거.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이행적 종속&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A -&amp;gt;B , B -&amp;gt; C 이면 A -&amp;gt; C이다&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 종속 관계&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부분집합 기호인지 화살표인지.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;네트워크 보안&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IPv6&lt;/li&gt;
&lt;li&gt;라우터, 스위치, 리피터 비교&lt;/li&gt;
&lt;li&gt;SPICE(Software Process Improvement and Capability dEtermination, 소프트웨어 처리 개선 및 능력 평가 기준)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;DES는 블록 암호화인가?&amp;nbsp; 몇 비트인가?&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키 길이는 56bit&lt;/li&gt;
&lt;li&gt;블록 크기는 64bit&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subnet 계산&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FCFS(선입선출)&lt;/li&gt;
&lt;li&gt;세 번째 영역의 5번째 주소는?&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSI 각 계층의 데이터 단위&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전송 계층
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP인 경우 Segment(세그먼트)&lt;/li&gt;
&lt;li&gt;UDP인 경우: Datagram(데이터그램)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네트워크 계층: Packet(패킷)&lt;/li&gt;
&lt;li&gt;데이터링크 계층:&amp;nbsp; Frame(프레임)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH vs 텔넷&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;SSH&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;가 Telnet 보다 신식&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 프로토콜&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RIP(Routing Information&amp;nbsp; Protocol, 라우팅 정보 프로토콜)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소규모 네트워크용 거리 프로토콜&lt;/li&gt;
&lt;li&gt;종류: &amp;nbsp;IGP(Interior Gateway Protocol)&lt;/li&gt;
&lt;li&gt;방식: 거리 벡터 라우팅&lt;/li&gt;
&lt;li&gt;소규모 네트워크 내 라우팅에 쓰인다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;BGP(Border Gateway Protocol, 경계 경로 프로토)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;종류: EGP(Exterior Gateway Protocol)&lt;/li&gt;
&lt;li&gt;방식: 경로 벡터 라우팅&lt;/li&gt;
&lt;li&gt;대규모 네트워크에서 쓰인다.&lt;/li&gt;
&lt;li&gt;자율 시스템 간 라우팅에 쓰인다&lt;/li&gt;
&lt;/ul&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 공격 용어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ping of death: 패킷 크기를 크게해서 공격 대상의 네트워크 마비.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 보안 기능 적용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IPsec&lt;/li&gt;
&lt;li&gt;S-HTTP&lt;/li&gt;
&lt;li&gt;SSL&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAS(Network Attached Storage, 네트워크 결합 스토리지)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버와 저장장치를 네트워크를 통해서 연결하는 방식.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로그래밍&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python에서 print() 하면 줄바꿈이 되는지?&lt;/li&gt;
&lt;li&gt;COBOL (어떤 문제의 선택지 중 하나였음)&lt;/li&gt;
&lt;li&gt;locality 지역성&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C 프로그래밍&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1720403094607&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char?? []s = &quot;c college&quot;
int *p = s;

for(int i = s.size - 2; i&amp;gt;= 0; --i){
	print('%d', *s + i)
}&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;br /&gt;Java&amp;nbsp; 에 대한 설명&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;c언어 처럼 구조체가 존재한다 (X)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기억장치 관리 - 배치전략&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;best fit 으로 했을 때 결과.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자격증</category>
      <category>2024 3회 정처기</category>
      <category>2024 정처기 필기 기출</category>
      <category>강남 솔데스크</category>
      <category>정처기 기출</category>
      <category>정처기 필기  기출</category>
      <category>정처기 후기</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/622</guid>
      <comments>https://oranthy.tistory.com/622#entry622comment</comments>
      <pubDate>Mon, 8 Jul 2024 11:50:16 +0900</pubDate>
    </item>
    <item>
      <title>53회 SQLD (2024 05 25)  합격 후기 및 오답정리</title>
      <link>https://oranthy.tistory.com/619</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;53회 SQLD 시험 합격 후기 &lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이번 시험은 기출 문제에 비해서 어려운 문제가 많았다. 한 달 동안 2023 이기적 sql 교재 하나로만 공부했는데 다시 돌아간다면 노랭이 + 다른 기본서를 참고해서 두 달 정도 깊게 공부할 것이다. 시험을 보다가 2023 교재에는 없는 피벗 테이블 문제가 하나 나와서 당황했다ㅠㅠ&lt;br /&gt;(교재가 오타도 많고 별로라 느꼈는데 그래도 합격 점수(78점)를 받은 걸 보면 교재가 아주 별로는 아님)&lt;br /&gt;5만원 내고 응시했는데 결과도 한 달 뒤에 나오고 시험지를 가져올 수 없었다. 아쉬운대로 카페에서 답을 맞춰보며 도움이 많이 됐다. - https://cafe.naver.com/sqlpd&lt;br /&gt;참고로 정처기와는 다르게 시험 장소가 근처에 많지 않아서 접수 첫 날에 바로 하는 걸 추천한다.&lt;br /&gt;----------- 출제 유형 ---------&lt;br /&gt;테이블 간에 같은 row가 있는데 JOIN 결과에 하나만 포함되는지, 둘다 포함되는지&lt;br /&gt;묵시적 형변환(ex) 10 + '10')&lt;br /&gt;NULL이 포함된 로우가 조인 결과로 카운트 되는지, UNION, 순위(ROWNUM, RANK, TOP, ...),&lt;br /&gt;JOIN, NULL과 관련 모든 내용&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;인조 식별자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인조 식별자를 만든 경우, 반드시 인조 식별자를&amp;nbsp; 사용해야 한다. (X)&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;size18&quot;&gt;&lt;b&gt;파티셔닝 RANGE, ROWS 차이점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RANGE
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;price 값을 기준으로 정렬한 다음, current row의 값 price 에 대해&lt;/li&gt;
&lt;li&gt;price - 100 ~ price + 200 범위에 있는 데이터만 가져온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;SUM(amount) OVER (
     ORDER BY price
     RANGE BETWEEN 100 PRECEDING AND 200 FOLLOWING
 ) AS running_total
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ROWS
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;price 값을 기준으로&lt;span&gt; &lt;/span&gt;&lt;/span&gt;정렬한 다음, current row를 기준으로 전전행 부터(2 preceding) 다음 행(1 following)에 해당하는 row 범위에 있는 데이터를 모두 가져온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;SUM(amount) OVER (
     ORDER BY price
     ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING
 ) AS running_total
&lt;/code&gt;&lt;/pre&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정규식 1) REGEXP_SUBSTR 이메일&lt;/li&gt;
&lt;li&gt;정규식 2) REGEXP_SUBSTR (ABAA)&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;LTRIM(&amp;rsquo;abcxyxabx&amp;rsquo;, &amp;lsquo;cba&amp;rsquo;)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 번째 매개변수의 값을 쪼갠다. c, b, a&lt;/li&gt;
&lt;li&gt;첫 번째 매개변수의 앞에서부터 c, b, a 가 아닌 문자가 나오면 그 문자의 앞부분을 제거한다.&lt;/li&gt;
&lt;li&gt;결과:&amp;nbsp; xyxabx&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;UNION 으로 연결했을 때, 각 쿼리에 GROUP BY, ORDER BY 절을 적용 가능한가?&amp;nbsp; (노랭이 기출)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ORDER BY: X&amp;nbsp; 마지막에 딱 한번만 적용 가능함.&lt;/li&gt;
&lt;li&gt;GROUP BY: O&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;카르테시안 조인&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;select count(*) from a , b where 조건;&lt;br /&gt;a 테이블에 [1, 3] 로우 1개 존재&lt;/li&gt;
&lt;li&gt;b 테이블에 [1, 3] 로우 3개 존재&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &amp;nbsp;b의 중복된 로우 3개 모두 카운트된다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;b&gt;UNPIVOT&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PIVOT: 행 데이터를 열 데이터로 바꾼다.&lt;/li&gt;
&lt;li&gt;UNPIVOT: 열 데이터를 행 데이터로 바꾼다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;CARTESIAL JOIN&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SELECT * FROM a, b WHERE a.col1 = b.col1 and a.col2 = b.col2 and a.col3 = b.col3;&lt;/li&gt;
&lt;li&gt;A, B 테이블을 JOIN 하려는데 [1, 2, NULL] 도 카운트 되는가? (X)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;a.col3 , b.col3 모두 NULL인 경우, a.col3 = b.col3 결과는 FALSE&lt;/li&gt;
&lt;/ul&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;NATURAL JOIN&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A와 B 테이블 A테이블에 [1, 3] 한 개, B테이블에는 [1,3] 가 5개인 경우&lt;/li&gt;
&lt;li&gt;COUNT(*)결과: 5&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;b&gt;ALIAS&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TABLE ALIAS를 선언한 경우 SELECT 절에 테이블명을 직접 쓰지 말고 선언한 ALIAS를 적용해야한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;계층형 질의가 있는 쿼리 실행 순서&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계층형 구조를 먼저 형성하고 나중에 order by를 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; NOT IN(1, 2, NULL) 의 결과&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SELECT * WHERE NOT IN(1, 2, NULL) 의 결과는 ?&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;테이블 구조 변경&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 칼럼의 크기를 10으로 설정한 다음 칼럼의 크기를 더 큰 사이즈 / 더 작은 사이즈로 변경 가능한가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작은 사이즈로 바꾼다면 이미 큰 값으로 데이터가 들어간 로우는 어떻게 되는지?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어떤 칼럼에 대해 null 이 가능하도록 설정한 다음, notnull로 설정을 바꾼다면&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;바꾼다면 기존에 null 값이 들어간 row에 에러 발생??&lt;/li&gt;
&lt;/ul&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;DROP 과 DELETE 차이점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선지: WHERE 절이 없는 DELETE문은 DROP과 같다.&amp;nbsp; (X)&lt;/li&gt;
&lt;li&gt;DROP은 테이블을 CREATE 하기 이전으로 돌아가고 DELETE는 테이블의 형태는 남아있으나 모든 로우만 지우는 기능이다. 따라서 다르다.&lt;/li&gt;
&lt;li&gt;그렇다면 WHERE 절이 없는 DELETE는 테이블을 CREATE 하기 전으로 돌아가는 TRUNCATE와&amp;nbsp; 같은 결과를 가져올까? &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(X)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WHERE 절이 없는 DELETE&lt;/li&gt;
&lt;li&gt;TRUNCATE:&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ROLLBACK&amp;nbsp; 결과&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1717119756019&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
INSERT INTO TABLE50 VALUES(1);
INSERT INTO TABLE50 VALUES(2);
INSERT INTO TABLE50 VALUES(3);
SAVEPOINT SQL1;
INSERT INTO TABLE50 VALUES(4);
INSERT INTO TABLE50 VALUES(5);
COMMIT;
INSERT INTO TABLE50 VALUES(6);
ROLLBACK TO SAVEPOINT SQL1;

SELECT * FROM TABLE50;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이번 시험의 마지막 문제로 기억한다.&amp;nbsp;카페에서는 예상 답이 {1, 2, 3, 4, 5}&amp;nbsp; {1, 2, 3, 4, 5, 6} 으로 답이 나뉘었다.&lt;/li&gt;
&lt;li&gt;문제를 풀면서 롤백에서 에러가 날 거라는 예상을 하지 못하고 1, 2, 3이 출력될거라 잘못 생각했다.&lt;/li&gt;
&lt;li&gt;(Oracle 기준) 이론적으로는 롤백에서 예외가 터지면서 commit이 완료된 5까지 출력되고 6은 삭제되어야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;Oracle에서는 &lt;/span&gt;autocommit = off 가 기본값이므로 &lt;/span&gt;1, 2, 3, 4, 5 가 출력되어야 정상이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ROLLBACK: 해당 트랜잭션이 시작되기 전 상태로 되돌린다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 저장점으로 롤백 가능.&lt;/li&gt;
&lt;li&gt;하지만, 이전에 실행된 트랜잭션의 안에 있는 SAVEPOINT로 롤백하는 건 불가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;COMMIT: 트랜잭션의 모든 변경사항을 DB에 저장하고나서 트랜잭션을 종료한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;COMMIT 하고 나면 커밋된 트랜잭션 내에 설정한&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SAVEPOINT가 모두 무효화&lt;/b&gt;된다.&lt;/li&gt;
&lt;/ul&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MySQL , MariaDB&amp;nbsp; 롤백 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 위의 롤백 문제를 여러 DB에서 테스트하다가 다음과 같은 문제를 확인했다.&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;b&gt;문제점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;PostgreSQL&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;MariaDB에서&amp;nbsp; autocommit = off 로 설정하고 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;동일한 쿼리를 실행했으나&lt;/span&gt; 다음과 같이 결과가 달랐다.&lt;/li&gt;
&lt;li&gt;DBeaber 툴 문제인가 싶어서 터미널로도 테스트했으나 문제가 있었다.&lt;/li&gt;
&lt;li&gt;MySQL 계열 DB 에서는 autocommit 설정이 무시되고 전체 트랜잭션이 취소되지 않았다. 롤백 문장 하나만 취소됐다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;MySQL&lt;/span&gt;, MariaDB: autocommit = off로 설정하더라도, 트랜잭션에서 에러가 나는 경우 &lt;b&gt;전체 트랜잭션을 롤백하지는 않는다.&lt;/b&gt; ( &amp;harr; 트랜잭션의 원자성 )&lt;/li&gt;
&lt;li&gt;Oracle, MsSQL, PostgreSQL: autocommit = off 로 설정하는 경우, 트랜잭션에서 에러가 나면 전체 트랜잭션 내용을 롤백한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jr3ya/btsHLe8Ti3g/vKmxFJmqZWVKpf026Om7T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jr3ya/btsHLe8Ti3g/vKmxFJmqZWVKpf026Om7T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jr3ya/btsHLe8Ti3g/vKmxFJmqZWVKpf026Om7T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJr3ya%2FbtsHLe8Ti3g%2FvKmxFJmqZWVKpf026Om7T1%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;1060&quot; height=&quot;606&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;606&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Note&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; MySQL&lt;/b&gt;은&lt;b&gt; 단순함, 성능을 최우선 가치&lt;/b&gt;로 두고 설계 되었다. 따라서 복잡한 에러 핸들링 로직이 자동화되어있지 않고 사용자가 명시적으로 롤백해야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 쿼리에서는 'INSERT 6' 이 부분을 롤백하도록 개발자가 설정해야 한다.&lt;/li&gt;
&lt;li&gt;그러면 autocommit 기능이 왜 있을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MySQL에서 예외 발생 시 자동 롤백하는 방법?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장 프로시저에서 에러 핸들러를 사용해서 에러 발생 시 트랜잭션을 롤백하도록 설정해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이와 다르게 다른 DB(Oracle, PostgreSQL)는 에러가 나면 자동으로 전체 트랜잭션을 롤백한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그럼 MySQL은 autocommit = off 로 설정해도 autocommit = on 인 상태로 동작한다는 뜻?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션의 특성 ACID 중 원자성(Atomicity)를 만족하지 않는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;참고 링크&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://dev.mysql.com/doc/refman/8.0/en/innodb-error-handling.html&quot;&gt;InnoDB Error Handling&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #555555; text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://dev.mysql.com/doc/refman/8.0/en/commit.html&quot;&gt;START TRANSACTION, COMMIT, and ROLLBACK Statements&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;혹시 잘못된 부분이 있으면 댓글 부탁드립니다 ^^&lt;/p&gt;</description>
      <category>자격증</category>
      <category>53회 sqld 합격</category>
      <category>autocommit  롤백</category>
      <category>commit rollback</category>
      <category>db commit rollback</category>
      <category>mysql atomicity</category>
      <category>mysql autocommit off rollback</category>
      <category>mysql mariadb   롤백 autocommit</category>
      <category>mysql 원자성</category>
      <category>SQLD 합격 후기</category>
      <category>SQLD 후기</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/619</guid>
      <comments>https://oranthy.tistory.com/619#entry619comment</comments>
      <pubDate>Sun, 26 May 2024 00:10:55 +0900</pubDate>
    </item>
    <item>
      <title>[12월 5주차] Vue.js 3 상위 컴포넌트 &amp;harr; 하위 컴포넌트 데이터 전달 방법</title>
      <link>https://oranthy.tistory.com/610</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUXJJ/btsCUxMvsyX/0F8yMmziPpH2NaHxOKHIS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUXJJ/btsCUxMvsyX/0F8yMmziPpH2NaHxOKHIS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUXJJ/btsCUxMvsyX/0F8yMmziPpH2NaHxOKHIS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUXJJ%2FbtsCUxMvsyX%2F0F8yMmziPpH2NaHxOKHIS0%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;598&quot; height=&quot;258&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;258&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;blockquote data-ke-style=&quot;style2&quot;&gt;얼마 전부터 회사에서 IoT 관련 웹 페이지를 만들고 있습니다.&lt;br /&gt;그림과 같이 최상위 컴포넌트(Page) 안에 검색 컴포넌트(SearchForm)와 목록 컴포넌트(List)가 있고 그 안에 또 자식 컴포넌트(Modify)가 있는 형태입니다.&lt;br /&gt;검색폼에 조건을 넣어서 조회하던 도중, 디바이스 정보를 등록하거나 수정한 다음, (검색 조건을 그대로 적용한) 새로운 목록이 조회되는 기능을 만들려고합니다. 그러기 위해서는 검색 정보(searchInfo)를 Page로 전달하고 또 다시 하위로 전달하는 구조가 필요합니다.&lt;br /&gt;Vue.js 3에서 컴포넌트 간에 데이터를 주고 받는 방법을 정리하며 살펴보겠습니다.&amp;nbsp;&lt;/blockquote&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;b&gt;하위 컴포넌트(DeviceSearchForm) &amp;rarr; 상위 컴포넌트(Page) &lt;/b&gt;데이터 보내기&lt;/b&gt;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;DeviceSearchForm 에서 &lt;b&gt;defineExpose&lt;/b&gt;()를 이용하면 Page(상위 컴포넌트)로 데이터를 전달할 수 있습니다.&lt;br /&gt;&lt;b&gt;ref&lt;/b&gt; 속성은 Vue 인스턴스에 대한 참조를 만드는 역할을 합니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1703942539844&quot; class=&quot;xml&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
&amp;lt;template&amp;gt;
    &amp;lt;el-form 
        ref=&quot;ruleFormRef&quot;
        :model=&quot;searchInfo&quot;
    ..검색 관련 필드 
    /&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
const ruleFormRef = ref(null);

const searchInfo = reactive({     // 검색 정보
    fromDate: &quot;&quot;,             
    toDate: &quot;&quot;,
    paging: true
});

defineExpose({
   ruleFormRef: ruleFormRef
});

&amp;lt;/script&amp;gt;&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;&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;b&gt;하위 컴포넌트(&lt;b&gt;&lt;b&gt;Device&lt;b&gt;SearchForm&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;) &amp;rarr; 상위 컴포넌트(Page) 데이터 받기&lt;br /&gt;&lt;b&gt; &lt;b&gt;상위&lt;/b&gt; 컴포넌트(&lt;b&gt;Page&lt;/b&gt;) &amp;rarr; &lt;b&gt;하위&lt;span&gt; &lt;/span&gt;&lt;/b&gt;컴포넌트(DeviceList) 데이터 보내기&lt;/b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;앞서 검색폼에서 보낸 데이터를 Page 컴포넌트에서 받을 차례입니다.&lt;br /&gt;&amp;lt;DeviceSearchForm ref=&quot;receivedData&quot; .. /&amp;gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; const receivedData = ref(null);&lt;br /&gt;위와 같이 선언하면 receivedData.value를 통해 수신한 데이터에 접근할 수 있습니다.&amp;nbsp;&lt;br /&gt;그 다음 Page 의 다른 하위 컴포넌트인 List, Edit에 해당 정보를 전달하려합니다.&amp;nbsp;&lt;br /&gt;DeviceSearchForm 으로부터 검색 정보를 받아서 watch()로 실시간 감시하면서 state.searchInfo에 저장하면 다음과 같이 DeviceList와 DeviceEdit 에 데이터를 보낼 수 있습니다.&amp;nbsp;&lt;br /&gt;handleSearch(검색 메서드) 를 검색하기 위한 코드가 있습니다. 하위 컴포넌트에서는 emits(&quot;searchDevice&quot;) 를 통해서 상위 컴포넌트의 handleSearch() 를 사용할 수 있습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1703932111646&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
&amp;lt;template&amp;gt;
    &amp;lt;DeviceSearchForm 
        ref=&quot;receivedData&quot;
        @searchDevice=&quot;handleSearch&quot;
    /&amp;gt;
    &amp;lt;DeviceList
        :searchInfo=&quot;state.searchInfo&quot;
        @searchDevice=&quot;handleSearch&quot;
    /&amp;gt;
    &amp;lt;el-button type=&quot;primary&quot; @click=&quot;dialogVisibleEdit = true&quot;&amp;gt;등록&amp;lt;/el-button&amp;gt;
    &amp;lt;el-dialog v-model=&quot;dialogVisibleEdit&quot; title=&quot;디바이스 등록&quot;&amp;gt;
        &amp;lt;DeviceEdit 
            :searchInfo=&quot;state.searchInfo&quot;
            @cancelDialog=&quot;cancelDialog&quot;
            @searchDevice=&quot;handleSearch&quot;
        /&amp;gt;
    &amp;lt;/el-dialog&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
const receivedData  = ref(null);	

const state = reactive({
  searchInfo: {}  // DeviceSearchForm으로부터 받아서 다시 Edit, List에 전달할 데이터
});

watch(() =&amp;gt; receivedData.value, // SearchForm 으로부터 받은 데이터
  (crr) =&amp;gt; {
    state.searchInfo = crr.ruleFormRef;
  }
)

const handleSearch = (data) =&amp;gt;{
    // 검색 메서드
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;상위&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴포넌트(&lt;b&gt;Page&lt;/b&gt;) &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하위&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;컴포넌트(DeviceList) 데이터 받기&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;상위&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴포넌트( &lt;b&gt;&lt;b&gt;DeviceList&lt;/b&gt;&lt;/b&gt; ) &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하위&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;컴포넌트(DeviceModify) 데이터 보내기&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;앞서 검색폼에서 보낸 데이터를 Page에서 받았고 Page 가 보낸 데이터를 다시 List 컴포넌트에서 받을 차례입니다.&lt;br /&gt;&lt;b&gt;defineProps&lt;/b&gt;(상위 컴포넌트에서 보낸 데이터 정의)를 이용하면 위에서 보낸 데이터명을 받을 수 있습니다.&amp;nbsp;&lt;br /&gt;그러면 메서드는 어떻게 받을까요? &lt;br /&gt;&lt;b&gt;defineEmits&lt;/b&gt;(상위 컴포넌트에서 보낸 메서드 정의) 안에 searchDevice를 정의하고, 검색하고 싶은 부분에서 emits(&quot;searchDevice&quot;, data)로 이벤트를 발생시킵니다. 그러면 상위에서 선언한 검색 메서드(handleSearch(data))를 사용할 수 있습니다.&amp;nbsp;&lt;br /&gt;List 컴포넌트에는 각 디바이스에 대한 수정 팝업을 띄울 수 있도록 &quot;수정&quot; 버튼이 있는데 이 버튼을 클릭했을 때 나오는 수정 팝업 코드는 아래와 같습니다.&lt;br /&gt;modifyDevice() 메서드 안을 보면 정상적으로 수정이 된 다음, emits()으로 디바이스 목록을 재조회할 수 있습니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1703945016140&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;hide/&amp;gt;
&amp;lt;template&amp;gt;
	...디바이스 목록
  &amp;lt;el-dialog title=&quot;디바이스 수정&quot; v-model=&quot;dialogVisibleModify&quot; class=&quot;data&quot;&amp;gt;
      &amp;lt;DeviceModify
        :searchInfo=&quot;searchInfo&quot;
        @cancelDialog=&quot;cancelDialog&quot;
        @modifyDevice=&quot;modifyDevice&quot;
        @searchDevice=&quot;searchDevice&quot;
      /&amp;gt;
    &amp;lt;/el-dialog&amp;gt;
&amp;lt;/template&amp;gt;

const emits = defineEmits([&quot;searchDevice&quot;]);

const props = defineProps({
  searchInfo:
    type: Object,
    required: true,
  }
})
const searchDevice = (data) =&amp;gt; {
  emits(&quot;searchDevice&quot;, data);
};

const modifyDevice = () =&amp;gt; {
    $axios
      .post($apiUrls.iot.device.modify, deviceInfo,  {
        headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
      })
      .then((res) =&amp;gt; {
        alert(&quot;수정되었습니다.&quot;);
        // TODO path 담기
        emits(&quot;searchDevice&quot;, {
          curPage: 1,
          searchInfo: props.searchInfo,
        })
        cancelDialog();
      })
      .catch((err) =&amp;gt; {
        console.log(err);
        alert(&quot;수정에 실패하였습니다&quot;);
      });
    });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;새로 알게 된 내용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 컴포넌트에서 하위 컴포넌트로 메서드, 데이터를 보내는 것은 익숙하게 해봤으나 반대로 데이터를 보내는 방법은 이번에 자세히 알게되었습니다.&amp;nbsp;&lt;br /&gt;그리고 하위 컴포넌트에서 정의한 메서드를 상위 컴포넌트에서 호출하는 것은 권장되지 않는 다는 걸 새로 알게 되었습니다. Vue 의 기본 설계 원칙 중 하나인 '단방향 데이터 흐름(One-Way Data Flow)'에 어긋나기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt; &lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Vue의 기본 설계 원칙&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;단방향 데이터 흐름&lt;span style=&quot;text-align: left;&quot;&gt;(One-Way Data Flow): 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하면 하위 컴포넌트에서는 이벤트를 발생시켜서 상위 컴포넌트에 결과를 전달하는 방식을 말한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;단일 파일 컴포넌트: 각 컴포넌트를 하나의 파일로 구성할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;컴포넌트 기반: 컴포넌트 기반 아키텍처를 채택한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;양방향 데이터 기반&lt;span style=&quot;text-align: left;&quot;&gt;(Two-Way Data Binding): Vue는&amp;nbsp; 입력 요소와 Vue 데이터 속성 간에 양방향 동기화를 제공한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 요소: &amp;lt;template&amp;gt; 안에 있는 요소 중 사용자가 입력할 수 있는 input 박스, 라디오 버튼, 체크박스 같은 것&lt;/li&gt;
&lt;li&gt;Vue 데이터: Vue가 관리하고 있는 정보(&amp;lt;script&amp;gt;&amp;nbsp;안에&amp;nbsp;정의된&amp;nbsp;변수)를 말한다.&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가상 DOM: Vue는 가상 DOM을 사용해서 성능을 최적화한다. 가상 DOM은 실제 DOM과 동기화되며, 변경된 부분만 실제 DOM에 적용해서 효율적으로 렌더링한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Render 함수: 템플릿 대신에 JavaScript를 사용해서 가상 DOM을 생성하는 방법을 제공한다. 보통은 &amp;lt;template&amp;gt; 안에 UI를 작성하지만 렌더링 로직이 복잡하면 &lt;span style=&quot;text-align: left;&quot;&gt;JavaScript를 사용하는 게 더 효율적인 경우가 있다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TITLE&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CON&lt;/li&gt;
&lt;li&gt;CON&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 일지</category>
      <category>Vue.js 3 상위 하위 데이터 전달</category>
      <category>상위 컴포넌트 하위 컴포넌트 값 전달</category>
      <author>계란 </author>
      <guid isPermaLink="true">https://oranthy.tistory.com/610</guid>
      <comments>https://oranthy.tistory.com/610#entry610comment</comments>
      <pubDate>Sun, 31 Dec 2023 01:25:01 +0900</pubDate>
    </item>
  </channel>
</rss>