Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore: ๐Ÿ›๐Ÿ”ง Modification and refactoring of the presigned URL creation and conversion logic #179

Merged
merged 26 commits into from
Oct 19, 2024

Conversation

psychology50
Copy link
Member

์ž‘์—… ์ด์œ 


์ž‘์—… ์‚ฌํ•ญ

1๏ธโƒฃ Swagger ์—…๊ทธ๋ ˆ์ด๋“œ ๋ฐ ์ฟผ๋ฆฌ ์ˆ˜์ •

image

  • ๋ฌธ์ž์—ด์— ์˜์กดํ•˜๋˜ type์„ ์—ด๊ฑฐ ํƒ€์ž…์œผ๋กœ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • chatroomId ๋ฟ ์•„๋‹ˆ๋ผ, chatId์™€ feedId๊ฐ€ optional query param์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ Controller ๊ณ„์ธต์—์„œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋กœ์ง ์ถ”๊ฐ€

/**
 * {@link kr.co.pennyway.api.apis.storage.dto.PresignedUrlDto.Req}์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” Validator
 */
@Slf4j
public class PresignedUrlDtoReqValidator implements Validator {
    @Override
    public boolean supports(@NonNull Class<?> clazz) {
        return PresignedUrlDto.Req.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(@NonNull Object target, @NonNull Errors errors) {
        PresignedUrlDto.Req req = (PresignedUrlDto.Req) target;

        if (ObjectKeyType.CHATROOM_PROFILE.equals(req.type()) && req.chatroomId() == null) {
            errors.rejectValue("chatroomId", "MISSING_CHATROOM_PARAMETER", "์ฑ„ํŒ…๋ฐฉ ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•ด ์ฑ„ํŒ…๋ฐฉ ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.");
        }
        if (ObjectKeyType.CHAT_PROFILE.equals(req.type()) && req.chatroomId() == null) {
            errors.rejectValue("chatroomId", "MISSING_CHAT_PROFILE_PARAMETER", "์ฑ„ํŒ… ํ”„๋กœํ•„ ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•ด ์ฑ„ํŒ…๋ฐฉ ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.");
        }
        if (ObjectKeyType.CHAT.equals(req.type())) {
            if (req.chatroomId() == null) {
                errors.rejectValue("chatroomId", "MISSING_CHAT_PARAMETER", "์ฑ„ํŒ… ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•ด ์ฑ„ํŒ…๋ฐฉ ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.");
            }
            if (req.chatId() == null) {
                errors.rejectValue("chatId", "MISSING_CHAT_PARAMETER", "์ฑ„ํŒ… ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•ด ์ฑ„ํŒ… ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.");
            }
        }
        if (ObjectKeyType.FEED.equals(req.type()) && req.feedId() == null) {
            errors.rejectValue("feedId", "MISSING_FEED_PARAMETER", "ํ”ผ๋“œ ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•ด ํ”ผ๋“œ ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.");
        }
    }
}
public class StorageController implements StorageApi {
    private final StorageUseCase storageUseCase;

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new PresignedUrlDtoReqValidator());
    }

    @Override
    @GetMapping("/presigned-url")
    @PreAuthorize("isAuthenticated()")
    public ResponseEntity<?> getPresignedUrl(@Validated PresignedUrlDto.Req request, BindingResult bindingResult, @AuthenticationPrincipal SecurityUserDetails user) {
        if (bindingResult.hasErrors()) {
            throw new CustomValidationException(bindingResult);
        }

        ...
    }
}
  • chatId, chatroomId, feedId๋ฅผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ „๋‹ฌํ•ด์ค˜์•ผ ํ•˜๋Š” type์ธ ๊ฒฝ์šฐ, ํ™•์‹คํ•˜๊ฒŒ ์˜ˆ์™ธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒ์„ฑ์ž์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ฉด, BeanResolveException์ธ๊ฐ€? ๋ญ, ์ด์ƒํ•œ ์˜ˆ์™ธ๊ฐ€ ๊ฐ์‹ธ์ง€๋Š” ๋ฐ”๋žŒ์— ์œ„์™€ ๊ฐ™์ด ์ถ”๊ฐ€ validation ์ฒดํฌ ๋กœ์ง์„ ๋ฐ˜์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ ์ถ”์ƒ ํŒฉํ† ๋ฆฌ ํŒจํ„ด ๋„์ž…

๊ตณ์ด ๋”ฐ์ง€์ž๋ฉด, Simple Factory Pattern์— ๊ฐ€๊นŒ์šด..

public interface PresignedUrlProperty {
    String imageId();

    String timestamp();

    String ext();

    ObjectKeyType type();

    /**
     * Presigned URL ์ƒ์„ฑ์„ ์œ„ํ•œ ๋ณ€์ˆ˜๋“ค์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
     * key๋Š” ๊ฐ ๋ณ€์ˆ˜๋ช…์„ lowerCamelCase๋กœ ๋ณ€ํ™˜ํ•œ ๊ฒƒ์ด๋‹ค.
     */
    Map<String, String> variables();
}

์šฐ์„ , ๊ฐ Type์ด ๊ณตํ†ต์ ์œผ๋กœ ๊ฐ–๋Š” ํ•„๋“œ๋“ค์„ ํ•„์ˆ˜์ ์œผ๋กœ ํฌํ•จํ•˜๋„๋ก ๊ฐ•์ œํ•˜๋Š” interface๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
Profile, Chat, ChatRoom ๋“ฑ์˜ UrlProperty๋Š” ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ํ•„์š”ํ•œ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

PresignedUrlPropertyFactory factory = PresignedUrlPropertyFactory.create(request.ext(), request.type())
                .userId(userId)
                .chatroomId(request.chatroomId())
                .chatId(request.chatId())
                .feedId(request.feedId())
                .build();

์ด๋Ÿฌํ•œ PresignedUrlProperty๋Š” PresignedUrlPropertyFactory๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


4๏ธโƒฃ presigned url ์ƒ์„ฑ ๋กœ์ง ์ฑ…์ž„ ์žฌ๋ถ„๋ฐฐ

public final class UrlGenerator {
    ...

    /**
     * S3์— ์ž„์‹œ ์—…๋กœ๋“œํ•  ํŒŒ์ผ์˜ URL์„ ์ƒ์„ฑํ•œ๋‹ค.
     *
     * @param property {@link PresignedUrlProperty}: Presigned URL ์ƒ์„ฑ์„ ์œ„ํ•œ Property
     * @return Presigned URL
     */
    public static String createDeleteUrl(PresignedUrlProperty property) {
        return applyTemplate(property.type().getDeleteTemplate(), property.variables());
    }

    /**
     * ์ž„์‹œ ๊ฒฝ๋กœ์—์„œ ์‹ค์ œ ๊ฒฝ๋กœ๋กœ ํŒŒ์ผ์„ ์ด๋™์‹œํ‚ค๊ธฐ ์œ„ํ•œ URL์„ ์ƒ์„ฑํ•œ๋‹ค.
     *
     * @param type      {@link ObjectKeyType}
     * @param deleteUrl ์ž„์‹œ ๊ฒฝ๋กœ์˜ URL
     * @return Presigned URL
     */
    public static String convertDeleteToOriginUrl(ObjectKeyType type, String deleteUrl) {
        Map<String, String> variables = extractVariables(type, deleteUrl);
        return applyTemplate(type.getOriginTemplate(), variables);
    }

    ...
}
  • ๊ธฐ์กด์— ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ํฉ์–ด์ ธ์žˆ๋˜ Url ์ƒ์„ฑ ๋กœ์ง๋“ค์„ ํ•œ ๊ณณ์œผ๋กœ ๋ชจ์œผ๊ณ , UrlGenrator๊ฐ€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌํ•ด์•ผ ํ•  ํŒŒ์ผ์ด ํ›จ์”ฌ ์ค„์—ˆ์œผ๋ฉฐ, ์ž˜๋ชป๋œ ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ ์‚ฌ๋ก€๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , enum ํด๋ž˜์Šค๊ฐ€ ํŒŒ์‹ฑ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋“ฑ ์ฑ…์ž„๊ณผ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ ์‹คํŒจ ๋กœ์ง์„ ๋ชจ๋‘ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

5๏ธโƒฃ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ๊ฐœ์„ 

public class PresignedUrlPropertyFactoryTest {
    ...

    @ParameterizedTest
    @EnumSource(ObjectKeyType.class)
    @DisplayName("๋ชจ๋“  ํƒ€์ž…์— ๋Œ€ํ•ด PresignedUrlProperty๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.")
    void createPropertyAllTypes(ObjectKeyType type) {
        PresignedUrlPropertyFactory factory = createValidFactory(type);
        PresignedUrlProperty property = factory.getProperty();

        assertNotNull(property);
        assertEquals(type, property.type());
        assertNotNull(property.imageId());
        assertNotNull(property.timestamp());
        assertEquals("jpg", property.ext());
    }

    ...
}
public class UrlGeneratorTest {
    ...

    @ParameterizedTest
    @EnumSource(ObjectKeyType.class)
    @DisplayName("๋ชจ๋“  ํƒ€์ž…์— ๋Œ€ํ•ด ์ž„์‹œ ์ €์žฅ URL์„ ์›๋ณธ URL๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.")
    void convertDeleteToOriginUrlAllTypes(ObjectKeyType type) {
        PresignedUrlProperty property = createDummyProperty(type);
        String deleteUrl = UrlGenerator.createDeleteUrl(property);
        String originUrl = UrlGenerator.convertDeleteToOriginUrl(type, deleteUrl);

        String expectedOriginUrl = createExpectedOriginUrl(type, property);
        assertEquals(expectedOriginUrl, originUrl, "For type: " + type);
    }

    ...
}

๋˜‘๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žฌ๋ฐœํ•˜์ง€ ์•Š์Œ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋ณด๊ฐ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
ํŠนํžˆ PropertyFactory์™€ UrlGenrator์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๊ผผ๊ผผํ•˜๊ฒŒ ์ˆ˜ํ–‰ํ•˜์—ฌ, ์ •์ƒ ๋™์ž‘์„ ๋ณด์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.


๋ฆฌ๋ทฐ์–ด๊ฐ€ ์ค‘์ ์ ์œผ๋กœ ํ™•์ธํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„

  • ์—†์Œ

๋ฐœ๊ฒฌํ•œ ์ด์Šˆ

  • ์—†์Œ

@psychology50 psychology50 added bug ๊ธด๊ธ‰ํ•˜๊ณ , ์ค‘์š”๋„๊ฐ€ ๋†’์€ ์ด์Šˆ refactoring ๋ฆฌํŒฉํ† ๋ง ์ž‘์—… fix ๊ธฐ๋Šฅ ์ˆ˜์ • labels Oct 18, 2024
@psychology50 psychology50 self-assigned this Oct 18, 2024
@psychology50 psychology50 changed the title Api: ๐Ÿ›๐Ÿ”ง Modification and refactoring of the presigned URL creation and conversion logic Ignore: ๐Ÿ›๐Ÿ”ง Modification and refactoring of the presigned URL creation and conversion logic Oct 19, 2024
@psychology50 psychology50 merged commit fcb54ff into dev Oct 19, 2024
1 check passed
@psychology50 psychology50 deleted the fix/get-presigned-url branch October 19, 2024 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug ๊ธด๊ธ‰ํ•˜๊ณ , ์ค‘์š”๋„๊ฐ€ ๋†’์€ ์ด์Šˆ fix ๊ธฐ๋Šฅ ์ˆ˜์ • refactoring ๋ฆฌํŒฉํ† ๋ง ์ž‘์—…
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant