<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>toy-klog</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 20 Aug 2023 09:09:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>toy-klog</title>
            <url>https://velog.velcdn.com/images/doi-h/profile/3fa5c5dc-0315-414a-9f7d-dd9f67289441/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. toy-klog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/doi-h" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[장바구니(cart) 기능 Redis 구현]]></title>
            <link>https://velog.io/@doi-h/%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88cart-%EA%B8%B0%EB%8A%A5-Redis-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@doi-h/%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88cart-%EA%B8%B0%EB%8A%A5-Redis-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sun, 20 Aug 2023 09:09:07 GMT</pubDate>
            <description><![CDATA[<h2 id="장바구니-crd-구현">장바구니 CRD 구현</h2>
<p>사용자에게 장바구니 기능 제공하는데 Mysql을 사용할 수도 있다. 하지만 비용임으로 상대적으로 저렴한 Redis 사용하기로 결정했다. 기능은 장바구니 생성, 장바구니 목록 읽기, 장바구니 목록 삭제 구현했다. 
redisConfig에서는 redisconnectionfactory, redistemplate, cachemanage를 기본적인 기능 셋팅했다. 이 외로 보통 스프링 코드처럼 dto, controller, service  기능 추가했고 jpa 대신 dao에서 redistemplate 메소드 사용했다. </p>
<h2 id="향후">향후</h2>
<p>다른 기능들 추가로 구현하고, 기능 테스트 하면서 다른 모듈에 레디스 추가 계획이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리팩토링] Query 횟수 줄이기 (Redis @Cacheable)]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-Query-%EC%B5%9C%EC%A0%81%ED%99%94-Redis-Cacheable</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-Query-%EC%B5%9C%EC%A0%81%ED%99%94-Redis-Cacheable</guid>
            <pubDate>Sat, 19 Aug 2023 09:14:44 GMT</pubDate>
            <description><![CDATA[<h2 id="cacheable-사용-전-후-비교postman">@Cacheable 사용 전 후 비교(Postman)</h2>
<p>@Cacheable를 roomController의 Getroom메소드에 적용하니 아래처럼 호출 API 호출 시간이 대략 5배 정도 빨라졌다. 콘솔 확인해보면 처음 요청땐 SQL 요청하고, 그 이후에는 SQL 요청하지 않는다. DB 부하를 분산시킬 수 있게 됐다. postman 실행 결과는 다음과 같다.</p>
<p>@Cacheable 사용 전 Room 정보 호출 결과
<img src="https://velog.velcdn.com/images/doi-h/post/ef332f54-48d4-4255-8461-d26b325b940e/image.png" alt=""></p>
<p>@Cacheable 사용 후 Room 정보 호출 결과
<img src="https://velog.velcdn.com/images/doi-h/post/0e685880-5529-46bf-8315-76e853b48547/image.png" alt=""></p>
<h2 id="구현-방법">구현 방법</h2>
<p>컨트롤러의 일부코드, DTO, gradle 수정해서 구현했다. 
컨트롤러에서는 ResponseEntity 이 코드를 직렬화 할 수 없기 때문에 제거 했고,
DTO 에서는 redis 저장 및 읽기 때를 위한 직렬화 인터페이스 상속 받고,
gradle에서는 클래스로더에서의 DTO 호환성 문제 발생으로 해당 패키지는 주석처리했다.</p>
<pre><code>//수정 전 

//RoomController

   @Cacheable(value = &quot;room&quot;, key = &quot;#id&quot;)
   @GetMapping(&quot;/id/{id}&quot;)
   public ResponseEntity&lt;RoomDto&gt; getRoom(@PathVariable Long id){
       Optional&lt;RoomDto&gt; roomDtoOpt = roomService.getRoomAndIncrementViewCount(id);
       return ResponseEntity.ok(roomDtoOpt.get());
   }

//RoomDto
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RoomDto{}


//gralde
developmentOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
</code></pre><pre><code>//수정 후 

//RoomController

   @Cacheable(value = &quot;room&quot;, key = &quot;#id&quot;)
   @GetMapping(&quot;/id/{id}&quot;)
   public RoomDto getRoom(@PathVariable Long id){
       Optional&lt;RoomDto&gt; roomDtoOpt = roomService.getRoomAndIncrementViewCount(id);
       return (roomDtoOpt.get());
   }

//RoomDto
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RoomDto implements Serializable {}



//gralde
//developmentOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
</code></pre><p>쿼리 최적화를 위해
Room 에서의 RUD 는 @Cacheable, @CachePut, @CacheEvict를 활용해서 캐싱 구현했다.</p>
<h2 id="향후">향후</h2>
<p>쿼리 최적화를 위해서
Room 에 대한 장바구니 기능 구현 계획이다. redistemplate 혹은 Redis jpa 사용 계획이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리팩토링] 쿼리 성능 개선 (Fetch Join , PageNation)]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-Fetch-Join-PageNation</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%BF%BC%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-Fetch-Join-PageNation</guid>
            <pubDate>Mon, 14 Aug 2023 06:10:52 GMT</pubDate>
            <description><![CDATA[<h2 id="기존-코드-findall과-40번-쿼리요청">기존 코드 findAll과 40번 쿼리요청</h2>
<p>현재 코드는 프론트엔드에서 모임 목록 페이지에 접근하면 1번 페이지 열리면서 가장 최근의 Room 데이터 10개 호출해서 렌더링한다.</p>
<p>Room 테이블은 HostUser, Category, RoomImage 테이블과 관계 맺고 있는 상태다. Dto 변환시 각 테이블 데이터가 필요하기에 10개 데이터 호출되면 각 테이블데이터까지 포함해서 약 40번 쿼리 발생했다. 문제다.</p>
<h2 id="fetch-join과-pagenation">Fetch Join과 PageNation</h2>
<p>1개의 데이터 호출시 Fetch Join 사용하면 연관된 테이블 쿼리를 1번으로 압축 시킬 수 있었다. findAll도 가능할 것이라 예상했다. 아래 처럼 서비스와 레포지토리 코드를 수정 후 실행했다. 하지만 에러 발생했다. </p>
<p>Fetch Join는 4개의 연관 엔티티 데이터를 가져오고, Pagenation은 10개의 로우 데이터를 Limit 등 제한 조건적용해서 데이터를 가져온다. 하지만 위 두 기능에서 데이터 로딩때 데이터 중복 및 예상되지 않은 데이터 꼬임 및 성능저하 발생할 수 있다고 한다. </p>
<p>그런 이유로 Jpa에서는 두 기능 함께 사용을 미지원 및 상황에 따라서 제한한다고 한다. 그럼 어떻게 하면 좋을까?  </p>
<pre><code>//service

    @Override
    public List&lt;RoomDto&gt; getRoomList(Integer page, Integer size) {
        PageRequest pageRequest = PageRequest.of(page-1, size, Sort.by(&quot;id&quot;).descending());
        Page&lt;Room&gt; roomsPage = roomRepository.findAllWithCategoryAndHostUsersAndImage(pageRequest);

        List&lt;Room&gt; rooms = roomsPage.getContent();
        if (rooms.isEmpty()) {
            return new ArrayList&lt;&gt;(); 
        }

        List&lt;RoomDto&gt; roomDtos = rooms.stream().map(r -&gt; roomMapper.toRoomDto(r)).collect(Collectors.toList());

        return roomDtos;
    }
</code></pre><pre><code>//repository

    @Query(&quot;SELECT DISTINCT r FROM Room r &quot; +
            &quot;LEFT JOIN FETCH r.category &quot; +
            &quot;LEFT JOIN FETCH r.hostUserList &quot; +
            &quot;LEFT JOIN FETCH r.roomImage&quot;
    )
    Page&lt;Room&gt; findAllWithCategoryAndHostUsersAndImage(Pageable pageable);
</code></pre><h2 id="batch-40---4">Batch (40 -&gt; 4)</h2>
<p>먼저 떠오른 생각은 모든 데이터 다 호출 후 Page구분 하려고 했다. 문제는 데이터가 쌓일수록 문제도 쌓일 것이라 판단했다. </p>
<p>결국 사용한 방법은 Fetch Join을 빼고 application-dev.yml에 Batch 값을 10으로 셋팅했다. 최대 여러 번 쿼리를 한 번에 묶어서 요청하는 방식인데 한 페이지에 10개씩 데이터 필요해서 10으로 할당했다.</p>
<p>이 후 Room 목록 10개 조회하니 40번의 쿼리는 4번으로 줄었다.(page로 인해 생긴 Count 쿼리 추가하면 5번)
임시적으로 이전보다는 쿼리 최적화 시켰다.</p>
<p>할 수 있으면 1번의 쿼리로도 가능할까 싶은데 아직은 잘 모르겠다. 나중에 알게되면 그 방법 사용할 계획이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리팩토링] 쿼리 개선 및 성능 테스트 (Fetch Join , N+1)]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%BF%BC%EB%A6%AC-%EA%B0%9C%EC%84%A0-%EB%B0%8F-%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8-Fetch-Join-N1</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%BF%BC%EB%A6%AC-%EA%B0%9C%EC%84%A0-%EB%B0%8F-%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8-Fetch-Join-N1</guid>
            <pubDate>Thu, 10 Aug 2023 07:06:12 GMT</pubDate>
            <description><![CDATA[<h2 id="fetch-join-전-후-코드-및-쿼리-비교">Fetch join 전 후 코드 및 쿼리 비교</h2>
<p>user 부분 리팩토링 중이다. 상황은 다음과 같다. </p>
<p>user entity는 userImage entity와 1:1 관계를 가진다. user는 6명이다. 코드는 아래와 같다. 작동 순서는 user list 가져온 후 User 참조형을 UserDto 로 바꾼다. userDto로 바꿀때 연관관계에 있는 UserImage를 가져온다. </p>
<pre><code>&lt;ReviewRepository&gt;

    List&lt;User&gt; findAllUserWithUserImage();



&lt;UserMapper&gt;

  default UserDto toUserDto(User user) {
        if (user == null) {
            return null;
        }

        return new UserDto(
                user.getId(),
                user.getUsername(),
                user.getUserImage().getProfile(),
                user.getEmail(),
                user.getDescription(),
                user.getRoleType()
        );
    }
</code></pre><p>이때 아무런 조치를 취해주지 않으면 콘솔에 쿼리는 userList 호출 쿼리 + 각 유저의 userImage 정보를 가져오느라 6번 추가 쿼리 발생한다. 총 7번 쿼리가 발생한다. 가령 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/doi-h/post/3c639b97-6858-4ea7-92c6-ee3f8b9000ca/image.png" alt=""></p>
<p>하지만 fetch join을 사용하면 쿼리는 아래와 같이 1번으로 줄어든다.</p>
<pre><code>&lt;UserRepository&gt;

@Query(&quot;SELECT u FROM User u LEFT JOIN FETCH u.userImage&quot;)
    List&lt;User&gt; findAllUserWithUserImage();
</code></pre><p><img src="https://velog.velcdn.com/images/doi-h/post/ba9deb8f-15e4-46c8-8f48-8485506d4947/image.png" alt=""></p>
<p>그럼 이렇게 쿼리가 줄어들면 뭐가 좋을까?</p>
<h2 id="fetch-join과-검색-속도">Fetch join과 검색 속도</h2>
<p>fetch join 적용 전 후 코드에 대해 포스트 맨에서 검색 테스트 해봤다. 테스트는 각각 1000번씩 요청 보내고 속도를 측정했다.</p>
<p>먼저 fetch join을 적용하지 않아서 N+1 발생하는 상황이다. 위에서 첨부한 사진처럼 1회 요청에 1+6회, 총 7회 쿼리가 날아간다. 그럼 1000번 요청은 1000 + 6000 = 7000 요청 발생했다. 속도는 아래 사진 처럼 약 24초다.
<img src="https://velog.velcdn.com/images/doi-h/post/66221e3f-d9c2-410b-be12-fa24c5e7d3bc/image.png" alt=""></p>
<p>다음은 fetch join 적용한 코드다. 1000번 요청에 1000개의 쿼리 발생했다. 약 20초 정도 걸렸다.
<img src="https://velog.velcdn.com/images/doi-h/post/37302180-0bbb-400f-9790-8dfe889b2da9/image.png" alt=""></p>
<p>1000번의 쿼리 발생에서 약 4초 정도 차이 발생했다. 실제 서비스 24/7 다양한 유저의 요청 환경에서는 위와같은 차이는 적지 않은 성능 및 비용 발생으로 예상된다.</p>
<p>그럼 아예 안전하게 모든 N+1 발생 레포지토리에 fetch join 쓰면 좋을까?</p>
<h2 id="fetch-join-비용">fetch join 비용</h2>
<p>정규화 된 환경에서 N+1 이 많이 발생하는 상황이라면 항상 fetch join을 사용할 것이다. 하지만 어느 순간 역정규화해서 한 테이블에서 검색하는게 fetch join 으로 인해 발생하는 join 비용보다 저렴할 상황이 발생할 수 있다. 이때는 과감히 정규화 + fetch join 을 버리고 역정규화를 선택하는게 좋다고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리팩토링] 중복 쿼리 제거로 인한 성능향상]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%A4%91%EB%B3%B5-%EC%BF%BC%EB%A6%AC-%EC%A0%9C%EA%B1%B0%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%B1%EB%8A%A5%ED%96%A5%EC%83%81</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%A4%91%EB%B3%B5-%EC%BF%BC%EB%A6%AC-%EC%A0%9C%EA%B1%B0%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%B1%EB%8A%A5%ED%96%A5%EC%83%81</guid>
            <pubDate>Wed, 09 Aug 2023 23:36:23 GMT</pubDate>
            <description><![CDATA[<h2 id="중복-쿼리-제거로-인한-성능-향상">중복 쿼리 제거로 인한 성능 향상</h2>
<p>컨트롤러 각 메소드에서 요청 유저 토큰 인증 유효성 체크 코드가 중복이었다. 요청 쿼리 중복 제거를 위해 jwt 필터에서 요청시 유저유효성 확인하게 하고 각 컨트롤러에서는 토큰에서 유저 정보 받아와서 사용하도록 중복 코드 제거했다. 또한 예외처리 로직을 전부 서비스 클래스로 옮겼다. 해당 코드 수정에 대한 성능 향상 체크하기 위해 포스트 맨으로 200번 요청 테스트 했다.</p>
<p>이전 코드
<img src="https://velog.velcdn.com/images/doi-h/post/b285002d-a4d9-4a6a-9c10-1b20c4aae924/image.png" alt=""></p>
<p>수정 코드
<img src="https://velog.velcdn.com/images/doi-h/post/e602caf7-6dd1-4674-9541-f0570b264780/image.png" alt=""></p>
<p>200번 요청에 기존 속도보다 약 10% 정도 속도 빨라졌다.</p>
<p>각 모듈에 만들어진 코드들에서 중복 쿼리 제거하면 이전보다 전체 서비스 속도는 빨라질 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리팩토링] 계획]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EA%B3%84%ED%9A%8D</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EA%B3%84%ED%9A%8D</guid>
            <pubDate>Mon, 07 Aug 2023 00:47:38 GMT</pubDate>
            <description><![CDATA[<h2 id="기능구현---리팩토링">기능구현 -&gt; 리팩토링</h2>
<p>meetup 프로젝트는 기능 구현에 중점을 두고 개발했다. 기능 작동되니 다음 단계는 코드의 가독성, 중복제거, shell script, 테스트 등 수정 계획이다.</p>
<p>한 주동안 어떻게 하면 리팩토링 할 수 있는 안목을 기를 수 있을까 찾아다녔다. 유튜브 둘러보고, 도서관에서 책들 훑고, 스프링 오픈소스 열어보기, 다른 동료의 코드리뷰 등 이것저것했다. 정말 좋은 컨텐츠가 많았지만 지금 이순간 가장 실용적이었던 것은 다른 프로젝트의 코드를 보고 그 패턴을 익히는 것이었다. </p>
<p>깃허브에서 java spring 으로 만들어진 백엔드 프로젝트를 찾아봤다. 보면서 내 코드와 비교하니 어디를 개선하면 좋을지 아이디어 떠올랐다. 참 많다... 이것말고도 고치고 고치고 고쳐야될 텐데 약간 기대된다. </p>
<h2 id="리팩토링-계획">리팩토링 계획</h2>
<ol>
<li>DB 정규화<ul>
<li>업로드 파일과 이미지 파일을 엔티티별로 따로 관리하고 있다. 코드도 각 엔티티별로 만들어서 3개씩 존재한다.중복있다. 엔티티와 코드를 1개로 합칠까 생각했다. 코드는 확실히 엄청 유사하니 한 모듈 내 콩통 로직으로 묶을 수 있다고 판단된다. 하지만 엔티티는 데이터의 일관성을 유지하기 위해서 그대로 두는게 낫겠다는 판단 든다. </li>
<li>코드 수정하다가 필요성 발견되면 DB 정규화 할 것이지만, 아직은 필요성 보이지 않는다.</li>
</ul>
</li>
</ol>
<ol start="2">
<li>업데이트<ul>
<li>부분 수정은 patch 메소드로 고치고, 전체 덮어쓰기는 put 쓰고, 영속성컨텍스트 더티체킹 기능 활용으로 내부에서 데이터 호출 후 수정하는 코드로 간단하게 업데이트 쪽은 수정해야겠다.</li>
</ul>
</li>
</ol>
<ol start="3">
<li>설정<ul>
<li>application.yml dev, prod, local 별 관리하는 환경셋팅
  -실행환경별 로그 출력 aop </li>
<li>ddl 대신 sql init + shell
  -어드민 유저 생성</li>
<li>테스트 환경은 h2 db 사용</li>
<li>properties묶음 관리 -  (mail, oauth, jwt, db, ...)</li>
<li>properties와 config는 분리해서 관리</li>
</ul>
</li>
</ol>
<ol start="4">
<li>코드 수정 및 추가 기능</li>
</ol>
<ul>
<li><p>생성자 사용시 static of 사용. pagenate 내장 클래스도 이 방법을 사용하고, 이를 사용하게 되면 생성자 생성시 내부에서 예외 체크 할 수 있다. 또한 특정 인스턴스와 관련없음을 명시해서 해당 메소드로 인한 상태 변화는 클래스만 된다는 것을 명시할 수 있어서 가독성 향상 될 것이라 생각한다.</p>
</li>
<li><p>try-catch를 줄임. 테스트 코드를 줄일 수 있고 가독성 향상 시킬 수 있고 기능 최적화 가능하다.</p>
</li>
<li><p>custom annotation은 기능</p>
</li>
<li><p>social login을 spring security 없이 구현 계획 </p>
</li>
<li><p>cookie manager 쿠키의 생성 및 파싱 등 관리하는 모듈을 만드는 게 가독성 및 유지관리 효율</p>
</li>
<li><p>stream().collect(), collectors, hashmap&lt;&gt; 등 collector 프레임워크를 최대한 적용. 기능 향상 및 코드 가독성</p>
<ul>
<li>&quot;dto::getter&quot; 이런 형식 사용. 매개변수 추론인듯?</li>
</ul>
</li>
<li><p>@query fetch join</p>
</li>
<li><p>@Lock</p>
<ul>
<li>낙관적락 비관적락</li>
</ul>
</li>
<li><p>@Transactional(readOnly = true)</p>
</li>
<li><p>mapper (obj -&gt; dto, dto-&gt;obj, dto 생성), 이전 코드는 각 엔티티 및 각 dto에서 맵핑 기능을 구현했지만 mapper라는 하나의 클래스에서 관리하게 하면 코드 가독성 및 유지보수 효율
 -@mapper() - A -&gt; Adto 없는 필드 기본값 셋팅가능(dto에서 @allargsconstrutor 때문인듯)</p>
<pre><code> - 셋팅안하면 못 씀
 - 참조값은 기본값 자동설정 어렵다네
 - 여러 참조값넣는데 겹치는 값 있을때 사용</code></pre><ul>
<li>일일히 객체 -&gt; 객체dto 값할당 방식 제거</li>
<li>만들려는 DTO에 해당하는 필드에 알아서 일치되는것만 넣어줌</li>
<li>@mapping이나 인자로 넣나 동작은 같은데, 로직이 복잡해서 명시적 표현</li>
</ul>
</li>
</ul>
<ul>
<li>javadoc </li>
</ul>
<ul>
<li><p>페이징 수정</p>
</li>
<li><p>service로직에서 R과 CUD를 분리. R은 인덱스 및 최적화 코드로 직접 관리할 수 있도록 구분해서 두고, 트랜잭션이 필요한 서비스만 따로 묶고, 트랜잭션 필요하지 않은 서비스 따로 묶어서 최소 3개의 덩어리로 구분하기 </p>
<ul>
<li><p>admin은 주로 유저 요청이 제대로 동작되지 않거나, 제제를 가한다는 전제에서 기능 추가</p>
</li>
<li><p>dto는 엔티티 존재 시 들어온 요청에 대한 데이터 , 응답할 데이터 최대 2개로 만들어서 domain/dto에서 관리. 엔티티 없으면 공통 dto폴더 내 개별관리.</p>
</li>
<li><p>생성자 빌더패턴 사용. 유연한 매개변수를 가진 생성자를 만들 수 있음.
-롬복의 빌더패턴 (cf. 기본생성자+셋터주입 , 생성자주입과 같이 객체만들기 위함이지만, 할당하는 객체 값이 어디에 되는지 가독성 향상 및 순서 어겨도됌), 빌더채턴은 내부적으로 기본생성자 생성+셋터주입
   -@NoArgsConstructor(access = AccessLevel.PROTECTED) 사용해도 빌더패턴사용가능함. 두개를 같이 쓰는게 캡슐화에 안전</p>
<pre><code>   -@Builder(allArgsConstructor = true) 빌더에 강조할수있음.
   -@Builder() - 매개변수 갯수가다른 객체 코드 작성할 필요 없이 사용가능.</code></pre><p>   -@builder() 만 사용한다면, 이는 기본생성자, 모든 값을 할당하는 생성자, 일부 값만 할당하는 생성자 를 만들수있단느 것을 의미하니까 @AllArgsConstructor와 @NoArgsConstructor(access = AccessLevel.PROTECTED) 를 쓰지 않는게 낫
   -이거 위 설명 틀릴수도 잇어직접테스트 해봐야됌.</p>
</li>
<li><p>domain에 equals, hashcode 오버라이딩 하고, isvalid 메소드 구현 및 셋터 내 사용.</p>
</li>
<li><p>@EqualsAndHashCode(onlyExplicitlyIncluded = true)          @EqualsAndHashCode.Include</p>
</li>
<li><p>@embeddable ( 다른 엔티티에서 재사용할 수 있는 값 타입을 정의할 때 사용)</p>
</li>
<li><p>swagger 이나 javadoc 관리 모듈로 구분하면 좋을듯</p>
</li>
<li><p>{} 형식 데이터 관리를 적극적으로 할 필요. 
@JsonUnwrapped -&gt; 멤버변수로 어떤 객체를 사용하는데, 이걸 JSON변환할때, 가령 api응답할때 가만히두면 한단계 깊이에서 객체만들어지는데 그걸 부모수준으로 끌어올려서 한단계 더 깊이 안들어가도 되게 함</p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>@EventListener 특정 동작발생시 마치 트리거처럼 작동되는 기능인듯. 비동기처리, 로그, 알림 등 여러기능으로사용</p>
</li>
<li><p>예외에 대한 메시지를 한 Enum에서 전부 관리하게 해서 코드 가독성 높이기</p>
</li>
</ul>
<ul>
<li><p>룸이나 유저 검색시 특정 글자 포함시 검색하도록</p>
</li>
<li><p>통계 관련 모듈</p>
</li>
<li><p>일반유저를 어드민 만드는 로직 추가</p>
</li>
<li><p>cookiemanager 처럼 datemanager(jwt, room) 구현</p>
</li>
<li><p>enum은 전부 enum폴더에서 관리하게 하고, 차라리 dto도 그렇게 해보는걸 고민</p>
</li>
<li><p>nginx, 캐싱</p>
</li>
<li><p>환경변수를 중복되지 않게 따로 한번에 관리 (code, test, docker, git action)</p>
</li>
</ul>
<ol start="5">
<li>테스트 코드</li>
</ol>
<ul>
<li>db init sql 환경</li>
<li>테스트 자체 도커 </li>
<li>모듈, 컨트롤러, 도메인, 서비스 별 테스트</li>
<li>mokito 단위테스트 , 스프링 통합테스트 부분 의도적 분리.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[중간 회고 2] DB 모델링  ]]></title>
            <link>https://velog.io/@doi-h/%EC%A4%91%EA%B0%84-%ED%9A%8C%EA%B3%A0-2-DB-%EB%AA%A8%EB%8D%B8%EB%A7%81</link>
            <guid>https://velog.io/@doi-h/%EC%A4%91%EA%B0%84-%ED%9A%8C%EA%B3%A0-2-DB-%EB%AA%A8%EB%8D%B8%EB%A7%81</guid>
            <pubDate>Thu, 27 Jul 2023 04:41:59 GMT</pubDate>
            <description><![CDATA[<h1 id="무지성-db-모델링">무지성 DB 모델링</h1>
<p>meetup 프로젝트를 시작하면서 가장 먼저 한 것은 구현할 서비스에 대한 DB 모델링이었다. 일단 브레인스토밍 하듯 필요할 것 같은 객체에 대해 나열하고, 동작할 것 같은 방식으로 관계 맺고, 이를 기준으로 엔티티 파일 생성했다.</p>
<p>모임이니까 당연히 유저와 모임 룸은 필요하다고 판단했다. 이것을 중심으로 유저는 업로드사진 테이블이 필요하다고 생각했다. 유저와 모임은 다대다 관계가 될 수 있으니 참여유저, 호스트유저, 리뷰, 호스트의 리뷰 작성 을 조인테이블로 만들었다. 모임 룸은 카테고리 테이블과 다대일로 관계를 만들었고 업로드파일 및 업로드 이미지 테이블들과는 일대일 관계를 만들었다. 유저의 경우 관리자 기능을 위해 공지사항 테이블과 일대다 관계를 맺고 공지사항 테이블은 이미지와 업로드파일 각 테이블과 일대일 관계를 맺었다. 모든 테이블에서 공통으로 쓰일 생성과 수정 시간에 대한 정보는 BaseEntity로 만들고 모든 테이블에서 상속받아서 구현했다. </p>
<p>만들면서 몇몇 의문이 떠올랐다. 유저와 룸 사이 조인테이블을 굳이 4개씩 만들 필요가 있으며, 특히 호스트 유저 테이블과 호스트유저의 댓글 테이블이 꼭 필요할까? 정규화에 어긋나는데 데이터 관리 및 보기 편할 것 같은 이유로 유저, 공지사항, 룸에 각각 업로드 내지는 이미지 테이블을 1:1로 각각 만들어줄 필요 있을까? 등 테이블 간 관계에서 각 테이블의 필요에 대한 정당성에 의문이 들었다. 설계의 기준을 적용할 수 있더라면 했겠지만 역량이 부족했다. 일단은 학습 위한 기능구현에 집중했다. </p>
<p>전반적으로 기능하는 프로젝트 만들고나서 다시 기존의 DB 모델링 결과를 둘러보니 무지성으로 DB 모델링 했던 기억이 떠올라 불편하다. 개선하고 싶은데 어떤 방식으로 하면 좋을지 고민중이다. 그 중 생각 및 적용해볼 만한 한가지 기준에 대해 알 수 있는 글 읽었다. 42 동료가 쓴 &quot;42체크인 DB 설계 – 정규화와 역정규화 (<a href="https://42place.innovationacademy.kr/archives/9463">https://42place.innovationacademy.kr/archives/9463</a>) &quot; 이 글이다. </p>
<h1 id="db-모델링-수정-기준-1---정규화와-역정규화">DB 모델링 수정 기준 1 - 정규화와 역정규화</h1>
<p>글 요약해보니 42 서울 체크인 시스템 클론코딩하면서 DB 모델링 시행착오 공유 내용이었다. 인상 깊은 내용은 다음과 같다. 무의식으로 항상 정규화하고, 객체구조와 DB 테이블 관계를 일치시켰지만, 핵심은 효율적인 문제해결이고 정규화와 역정규화는 문제해결의 과정일 뿐이라는 내용이 기억 남는다.</p>
<p>이 내용을 토대로 무지성으로 모델링한 테이블 구조를 살피니 다음 스탭이 보인다. 먼저는 해결하고자하는 문제가 무엇이고, 그것을 해결하기위해 빼도 될만한 부분은 어디인지 찾아봐야겠다. 그 부분을 발견한다면 역정규화는 당연히 될거고 객체와의 관계는 깨져도 괜찮겠다. </p>
<p>그렇지만 이 기준 말고도 여러 기준이 더 필요 할 것 같다. </p>
<h1 id="다른-db-모델링-기준은-">다른 DB 모델링 기준은 ...</h1>
<p>일단은 더 이것저것 알아봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DEQUE]]></title>
            <link>https://velog.io/@doi-h/DEQUE</link>
            <guid>https://velog.io/@doi-h/DEQUE</guid>
            <pubDate>Tue, 25 Jul 2023 08:28:42 GMT</pubDate>
            <description><![CDATA[<h2 id="deque-double-ended-queue">Deque (Double-Ended Queue)</h2>
<ul>
<li>일반적인 Queue와는 다르게 맨 앞과 맨 뒤에서 모두 삽입과 제거가 가능한 자료구조</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ARRAY]]></title>
            <link>https://velog.io/@doi-h/ARRAY</link>
            <guid>https://velog.io/@doi-h/ARRAY</guid>
            <pubDate>Tue, 25 Jul 2023 08:28:22 GMT</pubDate>
            <description><![CDATA[<ul>
<li>array<ul>
<li>• 같은 자료형을 가진 <code>연속된 메모리 공간</code>으로 이루어진 자료구조, 주소값으로 구성</li>
<li>• Index를 통한 Random access가 가능하므로 Constant Time <code>O(1)</code>에 접근이 가능하다.</li>
<li>static array = 생성될때 고정된 크기, 일반적 방법으로 추가 삭제 못함</li>
<li>dynamic array - 값 추가돼면 추가로 static array 새로 만들어 기존 배열 통째로 복사해 옮김 capacity</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[정렬]]></title>
            <link>https://velog.io/@doi-h/%EC%A0%95%EB%A0%AC-crwjvh30</link>
            <guid>https://velog.io/@doi-h/%EC%A0%95%EB%A0%AC-crwjvh30</guid>
            <pubDate>Tue, 25 Jul 2023 08:27:27 GMT</pubDate>
            <description><![CDATA[<ul>
<li>정렬에 대해 설명해주세요.<ul>
<li>==  정렬알고리즘은 일정 순서대로 열거하는 알고리즘을 말합니다. 버블정렬, 선택정렬, 삽입정렬, 병합정렬, 힙정렬, 퀵정렬이 있습니다. 정렬알고리즘이 중요한 이유는 탐색을 용이하게 합니다.<ul>
<li>버블정렬 = 인접한 두 요소를 비교해가면서 정렬. 한번 돌때마다 큰값을 계속 뒤로 보내면서 마지막 요소가 정렬됌. 거품올라오는듯. 시간복잡도는 교환까지 해야돼서 On^2, 정렬되어져 있으면 On</li>
<li>선택정렬 = 한번 돌때마다 가장 작은 숫자를 탐색하고, 가장 왼쪽부터 차례대로 정렬채우는 방식. On^2</li>
<li>삽입정렬 = 이미 정렬된 배열에서 자기 위치를 찾아 삽입한다. 정렬된 배열 구간의 값들과 전부비교. 최선 On, 최악 On^2, 평균 On^2</li>
<li>병합정렬 = 분할 후 병합하는데, 병합하는 과정에서 정렬. 숫자를 물리적으로 만절씩 분리될수없을떄까지 분리 후, 결합할때 양쪽 숫자 덩어리중 작은 숫자를 하나씩 병합하면서 정렬 O(nlogN)</li>
<li>힙정렬 =</li>
<li>퀵정렬 = 특정 요소를 기준점으로 잡고 기준점보다 작은 요소는 왼쪽, 기준점보다 큰 요소는 오른쪽으로 두고 각각 중앙을 향해 한칸씩 움직이는데, 로우가 피봇보다 크고, 하이가 피봇보다 낮으면 로우와 하이자리를 바꿔주고, 중앙을 향해 나아감, 로우와 하이가 뒤바뀔때 피봇과 하이의 위치를 바꿔줌. 이때 자리바뀐 피봇을 기점으로 왼쪽은 피봇보다 작은값, 오른쪽은 피봇보다 큰값임. 이를 분할해서 위과정을 반복함, 피봇에 따라 성능차이가 심한데, 최악 On^2, 평균 OnlogN</li>
</ul>
</li>
<li><img src="https://velog.velcdn.com/images/doi-h/post/6a40d234-e41a-487f-a2be-8d370ceb22fd/image.png" alt="">
<img src="https://velog.velcdn.com/images/doi-h/post/fcb5a5d6-0107-44ab-b9ab-2a0f7b5ad0a7/image.png" alt=""></li>
</ul>
</li>
</ul>
<pre><code>정렬코드

- 버블정렬, 선택정렬, 삽입정렬, 퀵정렬, 병합정렬

    ```jsx
    #==========================
    #========= 버블정렬 =========
    print(&quot;============================\n========= 버블정렬 =========\n=============================&quot;)
    #==========================

    arr = [1,10,5,8,7,6,4,3,2,9]; print(&quot;정렬 전 =&quot;, arr)

    for i in range(len(arr)):
        for j in range(len(arr)-i-1):
            if arr[j] &gt; arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]

    print(&quot;정렬 후 =&quot;, arr)

    #==========================
    #========= 선택정렬 =========
    print(&quot;\n\n============================\n========= 선택정렬 =========\n=============================&quot;)
    #==========================

    arr = [1,10,5,8,7,6,4,3,2,9]; print(&quot;정렬 전 =&quot;, arr)

    for i in range(len(arr)):
        min_index = i
        for j in range(i+1, len(arr)):
            if arr[min_index] &gt; arr[j]:
                min_index = j
        arr[i], arr[min_index] = arr[min_index], arr[i]

    print(&quot;정렬 후 =&quot;, arr)

    #==========================
    #========= 삽입정렬 =========
    print(&quot;\n\n============================\n========= 삽입정렬 =========\n=============================&quot;)
    #==========================

    arr = [1,10,5,8,7,6,4,3,2,9]; print(&quot;정렬 전 =&quot;, arr)

    for i in range(1, len(arr)):
        for j in range(i, 0, -1):
            if arr[j] &lt; arr[j-1]:
                arr[j], arr[j-1] = arr[j-1], arr[j]
            else:
                break

    print(&quot;정렬 후 =&quot;, arr)

    #==========================
    #========= 퀵정렬 =========
    print(&quot;\n\n============================\n========= 퀵정렬 =========\n=============================&quot;)
    #==========================

    arr = [1,10,5,8,7,6,4,3,2,9]; print(&quot;정렬 전 =&quot;, arr)

    def quick_sort(arr, start, end):

        if start &gt;= end:
            return

        pivot = start
        left = start + 1
        right = end

        while left &lt;= right:

            while left &lt;= end and arr[left] &lt;= arr[pivot]:
                left += 1

            while right &gt; start and arr[right] &gt;= arr[pivot]:
                right -= 1

            if left &gt; right:
                arr[right], arr[pivot] = arr[pivot], arr[right]
            else:
                arr[left], arr[right] = arr[right], arr[left]

        quick_sort(arr, start, right-1)
        quick_sort(arr, right+1, end)

    quick_sort(arr, 0, len(arr)-1)

    print(&quot;정렬 후 =&quot;, arr)

    #==========================
    #========= 병합정렬 =========
    print(&quot;\n\n============================\n========= 병합정렬 =========\n=============================&quot;)
    #==========================
    #https://www.youtube.com/watch?v=zo-tz1vGutM

    arr = [1,10,5,8,7,6,4,3,2,9]; print(&quot;정렬 전 =&quot;, arr)

    def merge_sort(arr):

        if len(arr) &lt;= 1:
            return arr

        mid = len(arr) // 2
        left = merge_sort(arr[:mid])
        right = merge_sort(arr[mid:])

        i = 0
        j = 0
        k = 0

        # left와 right를 비교하면서 arr에 정렬된 값을 넣어준다.
        while i &lt; len(left) and j &lt; len(right):
            if left[i] &lt; right[j]:
                arr[k] = left[i]
                i += 1
            else:
                arr[k] = right[j]
                j += 1
            k += 1

        # left나 right가 남아있는 경우 arr에 넣어준다.
        if i == len(left):
            while j &lt; len(right):
                arr[k] = right[j]
                j += 1
                k += 1
        else:
            while i &lt; len(left):
                arr[k] = left[i]
                i += 1
                k += 1

        return arr

    arr = merge_sort(arr)
    print(&quot;정렬 후 =&quot;, arr)
    ```</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[DFS, BFS]]></title>
            <link>https://velog.io/@doi-h/DFS-BFS</link>
            <guid>https://velog.io/@doi-h/DFS-BFS</guid>
            <pubDate>Tue, 25 Jul 2023 08:26:48 GMT</pubDate>
            <description><![CDATA[<ul>
<li>DFS, BFS에 대해서 설명해주세요.<ul>
<li>==그래프 탐색하는 방법으로 DFS는 부모로부터 한쪽 방향의 맨 아래지식까지 쭉 탐색후, 그 직전의 부모의 자식탐색하는식으로 지그재그로 탐색하는 방법입니다. BFS는 부모로부터 직계자식을 탐색하고, 그다음 자식의 자식들을 탐색하는 방식으입니다. BFS는 큐와 맞고, DFS는 스택이랑 자료구조가 호환됌.</li>
<li></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[BSP 편향]]></title>
            <link>https://velog.io/@doi-h/BSP-%ED%8E%B8%ED%96%A5</link>
            <guid>https://velog.io/@doi-h/BSP-%ED%8E%B8%ED%96%A5</guid>
            <pubDate>Tue, 25 Jul 2023 08:26:07 GMT</pubDate>
            <description><![CDATA[<ul>
<li>BST의 최악의 경우의 예와 시간복잡도에 대해서 설명해주세요.<ul>
<li>== 예를들어 1부터 10까지 순차적으로 BST에 저장했다면, BST의 형태는 리스트와 같아집니다. 이 경우를 최악의 경우라고 하며 시간복잡도는 O(n)이 됩니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TREE]]></title>
            <link>https://velog.io/@doi-h/TREE</link>
            <guid>https://velog.io/@doi-h/TREE</guid>
            <pubDate>Tue, 25 Jul 2023 08:25:47 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Tree, Binary Tree, BST, AVL Tree에 대해서 설명해주세요.<ul>
<li>==트리는 배열이나 리스트나 스택이나 큐처럼 선형이 아니라 나무처럼 생긴 자료구조, 바이너리 트리는 자식이 무조건 2개인 트리 자료구조, 바이너리서치트리는 부모의 왼쪽은 부모보다작은 오른쪽은 부모보다큰 데이터로 정렬된 트리 자료구조, AVL tree와 red black tree는 밸런스트 트리. avl트리는 불균형 이진탐색트리의 불균형개선하는데 균형도 기준으로 균형트리 연산하는 트리.</li>
<li></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스택과 큐]]></title>
            <link>https://velog.io/@doi-h/%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%81%90</link>
            <guid>https://velog.io/@doi-h/%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%81%90</guid>
            <pubDate>Tue, 25 Jul 2023 08:25:09 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Stack, Queue에 대해서 설명해주세요.<ul>
<li>==스택은 선형 자료구조의 일종으로 마지막에 저장한 데이터를 가장 먼저 꺼내게 되는 LIFO(Last In First Out)방식의 자료구조 입니다. 스택의 사용 예시로는 웹 브라우저의 방문기록(뒤로가기), 실행 취소(undo) 등이 있습니다. 큐는 선형 자료구조의 일종으로 처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO(First In First Out)방식의 자료구조 입니다. 큐의 사용 예시로는 프린터의 인쇄 대기, 콜센터 고객 대기 시간 등이 있습니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리스트와 셋 차이]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-%EC%85%8B-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-%EC%85%8B-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Tue, 25 Jul 2023 08:24:56 GMT</pubDate>
            <description><![CDATA[<ul>
<li>List와 Set의 차이에 대해서 설명해주세요.<ul>
<li>==List는 <strong>중복</strong>된 데이터를 저장하고 순서를 유지하는 선형 자료구조이고, Set은 <strong>중복</strong>되지 않은 데이터를 저장할 수 있고, 일반적으로 순서를 유지하지 않는 선형 자료구조입니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[배열과 링크드 리스트 차이]]></title>
            <link>https://velog.io/@doi-h/%EB%B0%B0%EC%97%B4%EA%B3%BC-%EB%A7%81%ED%81%AC%EB%93%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@doi-h/%EB%B0%B0%EC%97%B4%EA%B3%BC-%EB%A7%81%ED%81%AC%EB%93%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Tue, 25 Jul 2023 08:24:42 GMT</pubDate>
            <description><![CDATA[<ul>
<li>배열과 링크드 리스트의 차이를 설명해주세요.****<ul>
<li>==배열은 메모리상에 순서대로 데이터를 저장합니다. 반면 링크드 리스트는 다음 데이터의 위치에 대한 포인터를 가지고 있는 구조입니다. 배열은 데이터를 인덱스로 조회할 수 있기 때문에 인덱스 조회성능이 높고, 데이터가 메모리에 순서대로 저장되어 있기 때문에, 비교적 빠르게 탐색을 수행할 수 있습니다. 데이터 삽입 및 삭제때는 데이터를 이동시켜야 함으로 시간이 더 걸릴수있습니다. 링크드 리스트는 중간에 데이터를 삽입하거나 삭제하는 것이 용이하다는 장점이 있습니다. 배열의 데이터접근 시간복잡도는인덱스 알면o1, 모르면 on, 삽입삭제는 0n, 링크드 리스트 데이터 접근은 On, 삽입삭제는 o1</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[시간복잡도 계산방법]]></title>
            <link>https://velog.io/@doi-h/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B3%84%EC%82%B0%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@doi-h/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B3%84%EC%82%B0%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 25 Jul 2023 08:24:24 GMT</pubDate>
            <description><![CDATA[<ul>
<li>시간복잡도 계산 방법<ul>
<li>== 컴퓨터 마다 성능이 다르기때문에, 실행시간(running time)이란 함수, 알고리즘 수행에 필요한 스탭 수로 계산합니다. 함수의 실행시간을 점근적 분석을 통해 실행시간을 단순하게 표현하며 점근적 표기법으로 표현한다.<img src="https://velog.velcdn.com/images/doi-h/post/2cfcdf99-1172-41a5-aeeb-17dff823eb7e/image.png" alt=""></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리스트]]></title>
            <link>https://velog.io/@doi-h/%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@doi-h/%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 25 Jul 2023 08:23:56 GMT</pubDate>
            <description><![CDATA[<ul>
<li>리스트 []<ul>
<li>== 순차적으로 중복가능 데이터 저장하는 자료구조 입니다. 순차적으로 데이터 접근할수있고, 다양한 데이터 유형 저장할 수 있습니다. 인덱스를 알지 못하면 검색시 O(n) 성능 떨어집니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[해시, 해시충돌 회피]]></title>
            <link>https://velog.io/@doi-h/%ED%95%B4%EC%8B%9C-%ED%95%B4%EC%8B%9C%EC%B6%A9%EB%8F%8C-%ED%9A%8C%ED%94%BC</link>
            <guid>https://velog.io/@doi-h/%ED%95%B4%EC%8B%9C-%ED%95%B4%EC%8B%9C%EC%B6%A9%EB%8F%8C-%ED%9A%8C%ED%94%BC</guid>
            <pubDate>Tue, 25 Jul 2023 08:23:41 GMT</pubDate>
            <description><![CDATA[<ul>
<li>해시<ul>
<li>== 해시는 키 밸류 형태로 데이터 저장하는 자료구조 입니다. 파일 등 데이터를 매개변수로 해시펑션을 통해 해쉬코드 만들고 인덱스로 반환하고 해쉬버킷에 데이터를 저장하는 자료구조 입니다.  해쉬코드로 다이렉트 데이터 접근가능. 하지만 해쉬버킷에 값이 많으면 검색시간이 O(n)까지 걸릴수잇음, 키는 많은데 해쉬코드는 정수라서 중복될수있음. 해쉬코드는 다른데 인덱스가 같아서 같은 방에 배정, 콜리전(충돌) 최소화를 위한 좋은 해쉬 알고리즘 필수.  시간복잡도는 보통 삽입, 삭제, 조회가 모두 0(1)을 가집니다.</li>
</ul>
</li>
</ul>
<ul>
<li><p>해시충돌회피방법 == 해쉬 버킷에 같은 값 이미 있을때 처리법</p>
<pre><code>     - open addressing - 다른 해시값에 저장
     - chaining - 해쉬 테이블이 원소 하나를 담는게 아니라 링크드 리스트를 담음.</code></pre><ul>
<li>해시버킷에 키(해쉬펑션에들어가는값)를 해쉬펑선기준(가ㅁ령, %3의 나머지)으로 해쉬(해쉬펑션의 결과)와 값(밸류) 을 저장해둔상태<ul>
<li>검색 = O(1), 삽입=O(1), 삭제 =O(1)</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[이진탐색트리, 자가균형트리]]></title>
            <link>https://velog.io/@doi-h/%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89%ED%8A%B8%EB%A6%AC-%EC%9E%90%EA%B0%80%EA%B7%A0%ED%98%95%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@doi-h/%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89%ED%8A%B8%EB%A6%AC-%EC%9E%90%EA%B0%80%EA%B7%A0%ED%98%95%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Tue, 25 Jul 2023 08:22:51 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>이진탐색트리</p>
<ul>
<li>==이진탐색트리는 왼쪽자식은 부모보다 작고, 오른쪽 자식은 부모보다 큰 이진트리입니다. 삽입 검색 삭제가 모두 트리 높이인 logN~N만큼의 시간복잡도를 가집니다. 하지만 편향될 위험이 있어서 자가 균형트리를 사용합니다.</li>
<li>왼족 자식은 부모보다 작고, 오른쪽 자식은 부모보다 큰 이진트리<ul>
<li>삽입, 검색 = 높이만큼의 시간복잡도 == logN ~ N(편향된 이진탐색트리)</li>
</ul>
</li>
</ul>
</li>
<li><p>자가균형트리</p>
<ul>
<li>== 자가균형트리는 편향된 이진탐색트리의 문제를 개선해서 편향을 줄인것이 자가 균형 트리 입니다. AVL, readblack tree가 있습니다</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>