Avatar
๐Ÿ˜‰

Organizations

  • Java์—์„œ Redis๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ CRUD Repository์™€ RedisTemplate์€ ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ˆ˜์ค€์˜ ์ถ”์ƒํ™”๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ฃผ์š” ์ฐจ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:


    โœ… 1. CRUD Repository (Spring Data Redis)

    RedisRepository, CrudRepository, PagingAndSortingRepository ๋“ฑ๊ณผ ๊ฐ™์ด Spring Data๊ฐ€ ์ œ๊ณตํ•˜๋Š” Repository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

    ๐Ÿ”น ์žฅ์ 

    • ๋†’์€ ์ถ”์ƒํ™” ์ˆ˜์ค€: ๋‹จ์ˆœํ•œ CRUD ์ž‘์—…์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ •์˜ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด, ๊ตฌํ˜„ ์—†์ด ์ž๋™์œผ๋กœ ๋™์ž‘.
    • ๊ฐœ๋ฐœ ์†๋„ ๋น ๋ฆ„: ๋ฐ˜๋ณต์ ์ธ ๋กœ์ง ์ค„์ผ ์ˆ˜ ์žˆ์Œ (findById, save, delete ๋“ฑ ๊ธฐ๋ณธ ์ œ๊ณต).
    • Spring Data์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉ ๊ฐ€๋Šฅ (์˜ˆ: ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ, ํŽ˜์ด์ง• ๋“ฑ).

    ๐Ÿ”น ๋‹จ์ 

    • ์„ธ๋ฐ€ํ•œ ์ œ์–ด ์–ด๋ ค์›€: ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋‚˜ ์ปค์Šคํ…€ ๋กœ์ง์€ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ค์›€.
    • ์ง๋ ฌํ™” ๋ฐฉ์‹ ๊ณ ์ •: ๊ธฐ๋ณธ์ ์œผ๋กœ JDK ์ง๋ ฌํ™”๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•˜๋ ค๋ฉด ์„ค์ • ํ•„์š”.

    ๐Ÿ”น ์˜ˆ์‹œ

    public interface UserRedisRepository extends CrudRepository<User, String> {
        Optional<User> findByUsername(String username);
    }
    

    โœ… 2. RedisTemplate

    Spring์—์„œ ์ œ๊ณตํ•˜๋Š” ์ €์ˆ˜์ค€ API๋กœ, ํ‚ค-๊ฐ’ ์ €์žฅ์†Œ๋กœ์„œ Redis๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Œ.

    Created Wed, 26 Mar 2025 11:11:03 +0900
  • feign client ์‚ฌ์šฉํŒ 1 - ์ƒ๋Œ€ ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ๋Œ€ ์ผ๋•Œ ๋žœ๋ค ํ˜ธ์ถœ

    load balancing ๋˜๊ณ  ์žˆ๋Š” ์ƒ๋Œ€์ธก์— ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉด์„œ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•

    • build.gradle
    ...
    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
    }
    ...
    
    • application.yml
    # ์ƒ๋Œ€ ์ธก์—์„œ 3๊ฐœ์˜ ์„œ๋ฒ„ LB๋˜๊ณ  ์žˆ์„ ๋•Œ 
    other-party-url:
      - 123.456.789.001
      - 123.456.789.002
      - 123.456.789.003
    
    • ServerProperties.java
    @Setter
    @Getter
    @Configuration
    @ConfigurationProperties(prefix = "other-party-url")
    public class OtherServerProperties {
    
        private List<String> serverUris;
    }
    
    • FeignConfig.java
    @RequiredArgsConstructor
    public class OtherServerFeignConfig {
    
        private final OtherServerProperties otherServerProperties;
    
        private final AtomicInteger counter = new AtomicInteger(0);
    
        @Bean
        public RequestInterceptor randomServerInterceptor() {
            return requestTemplate -> {
                List<String> serverUris = otherServerProperties.getServerUris();
    
                int index = counter.getAndIncrement() % serverUris.size();
                String selectedUrl = serverUris.get(index);
    
                requestTemplate.target(selectedUrl);
            };
        }
    }
    
    • FeignClient interface
    @FeignClient(name = "your-server", configuration = OtherServerFeignConfig.class)
    public interface OtherServerFeignClientV1 {
        
        ...
    }
    
    Created Wed, 26 Mar 2025 08:37:45 +0900

  • ๐Ÿš€ DispatcherServlet ์ดˆ๊ธฐํ™” ๊ณผ์ •

    1. ServletContainer๊ฐ€ DispatcherServlet์„ ๋กœ๋”ฉ

    Spring Boot ์•ฑ์„ ์‹คํ–‰ํ•˜๋ฉด, ๋‚ด์žฅ ํ†ฐ์บฃ์ด ๋œจ๊ณ  ์•„๋ž˜ ๊ณผ์ •์ด ์‹œ์ž‘๋ผ์š”:

    • Spring Boot๊ฐ€ ์ž๋™์œผ๋กœ DispatcherServlet์„ ์„œ๋ธ”๋ฆฟ์œผ๋กœ ๋“ฑ๋กํ•จ (/ ๋งคํ•‘)
    • DispatcherServlet์€ ์‚ฌ์‹ค HttpServlet์„ ์ƒ์†๋ฐ›์€ ํด๋ž˜์Šค
    • ์ด๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ โ†’ init() โ†’ ๊ทธ ์•ˆ์—์„œ initServletBean() ์‹คํ–‰๋จ

    2. WebApplicationContext ์ƒ์„ฑ ๋ฐ ์ดˆ๊ธฐํ™”

    • DispatcherServlet์€ ์ž์‹ ์˜ Spring WebApplicationContext๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
    • ์ด WebApplicationContext๋Š” @Controller, @RestController, @Service, @Repository ๋“ฑ์ด ๋“ฑ๋ก๋œ ๊ณต๊ฐ„
    GenericWebApplicationContext
          โ†‘
    AnnotationConfigWebApplicationContext
          โ†‘
    WebApplicationContext โ† DispatcherServlet์ด ์‚ฌ์šฉ
    

    DispatcherServlet์€ ์ด context๋ฅผ ํ†ตํ•ด ๋นˆ์„ ์ฐพ๊ณ , ๋ผ์šฐํŒ…์„ ์ˆ˜ํ–‰ํ•จ

    Created Wed, 26 Mar 2025 08:20:45 +0900
  • Spring์—์„œ๋Š” Servlet๊ณผ DispatcherServlet์ด ์›น ์š”์ฒญ ์ฒ˜๋ฆฌ์—์„œ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•ด์š”. ๊ฐœ๋…์„ ํ•˜๋‚˜์”ฉ ๋ช…ํ™•ํžˆ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ์š”.


    ๐Ÿงฑ 1. Servlet์ด๋ž€?

    โœ… ์ •์˜

    • ์ž๋ฐ” ์›น์—์„œ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์ปดํฌ๋„ŒํŠธ
    • HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ , HTTP ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ž๋ฐ” ํด๋ž˜์Šค
    • ํ†ฐ์บฃ(Tomcat) ๊ฐ™์€ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์‹คํ–‰๋จ

    โœ… ์—ญํ• 

    • ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์˜จ ์š”์ฒญ์„ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‘๋‹ต์„ ์ƒ์„ฑํ•จ
    • ์˜ˆ์ „์—๋Š” ๋ชจ๋“  ์š”์ฒญ์„ ์ง์ ‘ Servlet์—์„œ ์ฒ˜๋ฆฌํ–ˆ์–ด์š”.
    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
        protected void doGet(HttpServletRequest request, HttpServletResponse response) {
            response.getWriter().write("Hello from Servlet!");
        }
    }
    

    ์ด๊ฑด ๊ฑฐ์˜ Spring ์ด์ „์˜ ์ „ํ†ต์ ์ธ ์›น ๋ฐฉ์‹์ด์—์š”.

    Created Wed, 26 Mar 2025 08:19:45 +0900
  • Spring Boot์—์„œ Filter์™€ Interceptor๋Š” ๋ชจ๋‘ ์š”์ฒญ(Request)๊ณผ ์‘๋‹ต(Response)์„ ๊ฐ€๋กœ์ฑ„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด์ง€๋งŒ, ์—ญํ• , ์‹คํ–‰ ์‹œ์ , ์ ์šฉ ๋ฒ”์œ„์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ๊ฐ„๋‹จ + ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ์š”.


    ๐Ÿ”น 1. Filter (์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ)

    โœ… ๊ฐœ์š”

    • Servlet ์ŠคํŽ™์— ์ •์˜๋œ ๊ธฐ๋Šฅ
    • Spring๋ณด๋‹ค ๋” ํ•˜์œ„ ๋ ˆ๋ฒจ
    • DispatcherServlet ์ด์ „์— ์‹คํ–‰๋จ

    โœ… ์‚ฌ์šฉ ์˜ˆ์‹œ

    • ์ธ์ฆ, ์ธ์ฝ”๋”ฉ, ๋กœ๊น…, XSS ๋ฐฉ์–ด
    • ๊ณตํ†ต ์ „์ฒ˜๋ฆฌ/ํ›„์ฒ˜๋ฆฌ

    โœ… ์˜ˆ์ œ

    @Component
    public class MyFilter implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            System.out.println("Filter: ์š”์ฒญ ์ „");
            chain.doFilter(request, response);
            System.out.println("Filter: ์‘๋‹ต ํ›„");
        }
    }
    

    ๐Ÿ”น 2. Interceptor (์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ)

    โœ… ๊ฐœ์š”

    • Spring MVC์˜ ๊ธฐ๋Šฅ
    • DispatcherServlet ์ดํ›„์— ๋™์ž‘
    • ์ฃผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ ์ง„์ž… ์ „/ํ›„์— ๋กœ์ง ์ ์šฉ

    โœ… ์‚ฌ์šฉ ์˜ˆ์‹œ

    • ๋กœ๊ทธ์ธ ์ฒดํฌ
    • ๊ถŒํ•œ ๊ฒ€์‚ฌ
    • ์š”์ฒญ ๋กœ๊น…
    • ๊ณตํ†ต ์‘๋‹ต ๊ฐ€๊ณต

    โœ… ์˜ˆ์ œ

    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            System.out.println("Interceptor: ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „");
            return true; // false๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์•ˆ ๋„˜์–ด๊ฐ
        }
    }
    

    ๊ทธ๋ฆฌ๊ณ  ๋“ฑ๋ก์€ ์ด๋ ‡๊ฒŒ ํ•ด์•ผ ํ•ด์š”:

    Created Wed, 26 Mar 2025 08:15:45 +0900
  • github action sample

    spring boot

    • gradle build๋ฅผ ์‚ฌ์šฉํ•˜๋Š” spring boot๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ssh๋กœ

    • secret ๋ฅ˜๋Š” setting-> security์— Secrets and variables ๋‚ด action ๋ฉ”๋‰ด์—์„œ ์„ค์ •

    • ๋งˆ์ง€๋ง‰์— slack webhook์œผ๋กœ ๋ฐฐํฌ ์ƒํ™ฉ ์•Œ๋ฆผ

    • action ์„ค์ •์ด๋‚˜ slack webhoook url ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค๋ฅธ ๋ธ”๋กœ๊ทธ์—๋„ ๋งŽ์œผ๋‹ˆ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

      name: Build and Deploy
      
      on:
        push:
          branches:
            - develop
      
      jobs:
        build-and-deploy:
          runs-on: ubuntu-latest
      
          steps:
            # 1. SSH ํ˜ธ์ŠคํŠธ ํ‚ค ์ถ”๊ฐ€
            - name: Add SSH known hosts
              run: |
                mkdir -p ~/.ssh
                ssh-keyscan -H github.com >> ~/.ssh/known_hosts
      
            # 2. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ฒดํฌ์•„์›ƒ (์„œ๋ธŒ๋ชจ๋“ˆ ํฌํ•จ)
            - name: Checkout repository with submodules
              uses: actions/checkout@v3
              with:
                submodules: recursive
                ssh-key: ${{ secrets.PRIVATE_KEY_FOR_SUBMODULE }}
                fetch-depth: 0
      
            # 3. ์„œ๋ธŒ๋ชจ๋“ˆ ์ตœ์‹ ํ™”
            - name: Update submodules to the latest
              run: |
                git submodule update --init --recursive
                git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)
      
            # 4. JDK ์„ค์ •
            - name: Set up JDK 23
              uses: actions/setup-java@v3
              with:
                java-version: '23'
                distribution: 'temurin'
      
            # 5. Gradle ๋นŒ๋“œ
            - name: Build with Gradle
              run: ./gradlew clean build -x test
      
            # 6. ์„œ๋ฒ„๋กœ ํŒŒ์ผ ์ „์†ก
            - name: Deploy to Server
              env:
                SSH_PRIVATE_KEY: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
                DEPLOY_SERVER_IP: ${{ secrets.DEV_SERVER_IP }}
                DEPLOY_SERVER_USER: ${{ secrets.DEV_SERVER_USER }}
              run: |
                echo "DEPLOY_SERVER_IP: $DEPLOY_SERVER_IP"
                mkdir -p ~/.ssh
                echo "$SSH_PRIVATE_KEY" > ~/.ssh/github_action
                chmod 600 ~/.ssh/github_action
                ssh-keyscan -H "$DEPLOY_SERVER_IP" >> ~/.ssh/known_hosts
      
                # ๋นŒ๋“œ ํŒŒ์ผ ์ „์†ก
                scp -i ~/.ssh/github_action build/libs/*.jar $DEPLOY_SERVER_USER@$DEPLOY_SERVER_IP:/home/$DEPLOY_SERVER_USER/backend/
      
                # swagger rest doc json
                # scp -i ~/.ssh/github_action build/resources/main/restdocs/*.json $DEPLOY_SERVER_USER@$DEPLOY_SERVER_IP:/home/$DEPLOY_SERVER_USER/swagger-docs/
                find build/resources/main/restdocs -name "*.json" | grep . && scp -i ~/.ssh/github_action build/resources/main/restdocs/*.json $DEPLOY_SERVER_USER@$DEPLOY_SERVER_IP:/home/$DEPLOY_SERVER_USER/swagger-docs/ || echo "No JSON files to copy, skipping..."
      
            # 7. ์„œ๋ฒ„์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
            - name: Start Application
              env:
                DEPLOY_SERVER_IP: ${{ secrets.DEV_SERVER_IP }}
                SERVER_USER: ${{ secrets.DEV_SERVER_USER }}
                REPOSITORY_NAME: ${{ github.repository }}
              run: |
                PROJECT_NAME=$(echo $REPOSITORY_NAME | cut -d'/' -f2)
                ssh -i ~/.ssh/github_action $SERVER_USER@$DEPLOY_SERVER_IP << EOF
                cd /home/$SERVER_USER/backend
                chmod 700 /home/$SERVER_USER/backend/${PROJECT_NAME}*-exec.jar 
                sh /home/$SERVER_USER/bin/deploy.sh $PROJECT_NAME dev /home/$SERVER_USER 512m
                sh /home/$SERVER_USER/infra/swagger/swagger_script.sh restart ${PROJECT}-api.json
                EOF
      
            - name: action-slack
              uses: 8398a7/action-slack@v3
              with:
                status: ${{ job.status }}
                author_name: Pink Spider - dev
                fields: repo,commit,message,author # action,eventName,ref,workflow,job,took ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ
                mention: here
                if_mention: failure,cancelled
              env:
                SLACK_WEBHOOK_URL: ${{ secrets.DEV_SLACK_WEBHOOK_URL }} # required
              if: always() # Pick up events even if the job fails or is canceled.
      
    • deploy.sh

    Created Tue, 25 Mar 2025 18:01:05 +0900