-
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๋ฅผ ์ ์ฐํ๊ฒ ๋ค๋ฃฐ ์ ์์.
CreatedWed, 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 { ... }CreatedWed, 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๋ฅผ ํตํด ๋น์ ์ฐพ๊ณ , ๋ผ์ฐํ ์ ์ํํจCreatedWed, 26 Mar 2025 08:20:45 +0900 - Spring Boot๊ฐ ์๋์ผ๋ก
-
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 ์ด์ ์ ์ ํต์ ์ธ ์น ๋ฐฉ์์ด์์.
CreatedWed, 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๋ฉด ์ปจํธ๋กค๋ฌ๋ก ์ ๋์ด๊ฐ } }๊ทธ๋ฆฌ๊ณ ๋ฑ๋ก์ ์ด๋ ๊ฒ ํด์ผ ํด์:
CreatedWed, 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
CreatedTue, 25 Mar 2025 18:01:05 +0900 -