계속 새로운 문제가 나오고 있지만 2023년 3월 16일 기준으로 레벨 1 문제를 모두 해결해 봤다. 

 

언어는 모두 Java를 사용해서 풀었다.

 

원래 레벨 1을 조금 풀다가 이정도면 2 레벨로 들어가도 되겠다는 어리섞은 생각을 하게 되었다. 쉽게 풀렸던 몇몇 레벨 2문제를 만나서 그동안 몰랐던 거 같다. 

 

그래서 적어도 레벨 1 문제를 빠르게 다 풀어보자는 생각으로 문제를 풀기 시작했다. 레벨 1문제를 풀며 느낀 점을 정리해보려고 한다.

 

 

🔎 무조건 List! 무조건 Map!

내가 처음에 레벨 2를 풀기 시작했을 때 했었던 생각이다. 배열 보다는 역시 리스트를 써야지 이런 생각을 했었고, 실제로도 그렇게 풀었었다. 물론 List, Map, Set 모두 훌륭한 자료구조이다. 하지만 레벨 1문제를 풀면서는 먼저 문제에 접근할 때 배열로 해결할 수 있지 않을까라는 생각으로 접근을 하는 연습을 많이 했다.

 

그 이유는 분명 List와 Map이 효율적인 문제에서는 속도도 빠르고, 오히려 배열보다 효율성도 좋을 때가 많다. 하지만 대부분의 레벨 1 문제는 배열을 사용해서 해결할 수 있는 문제가 대부분이었고, 속도도 배열이 대부분 빨랐다. 이건 무조건 배열을 쓰라는 소리가 아니다. 배열을 써서 해결할 수 있는 문제라면 굳이 List나 Map을 쓰지 않아도 된다는 말이다.

 

 

🔎 StringBuilder를 활용하자

문제를 풀다보면 String 타입의 결과를 도출해 내는 문제가 종종 있다. 풀었던 문제 중에서 예시를 들자면 어떤 String 타입의 배열이 주어졌을 때, "Kim"이라는 문자열이 존재하는 index를 "김서방은 {index}에 있다"라는 형태의 문자열로 반환하는 문제였다.

 

배열에서 해당 문자열의 위치를 찾는 건 반복문을 돌려서 찾거나 List 타입으로 변환해서 indexOf() 메소드를 사용해 주면 쉽게 얻을 수 있다. 이제 이렇게 얻은 index를 "김서방은 "과 "에 있다"라는 문자열 사이에 넣어야 한다. 선택은 여러 가지가 있다.

 

# 그냥 더해서 반환하기

return "김서방은 " + index + "에 있다";

String은 이렇게 직접 더하는 연산을 사용하면 속도가 많이 느려진다. 레벨 1짜리 문제여서 별 차이는 느끼지 못할 정도이지만 피하는 것이 좋다.

 

 

# 포매팅을 사용하기

return String.format("김서방은 %d에 있다", index);

정수형 변수인 index를 포매팅(%d)을 이용해서 반환해 줄 수 있다. 실제 속도 차이로 봤을 때는 위에서 String을 그대로 더한 결과보다 빨랐다.

 

 

# StringBuilder 사용하기

return new StringBuilder("김서방은 ").append(index).append("에 있다").toString();

오늘의 핵심이다. StringBuilder는 append()라는 메소드로 계속 이어서 문자를 이어 붙일 수 있다. 그리고 마지막에 toString()을 써주면 만들어진 문자열을 String 타입으로 반환해 준다. 속도도 위에서 사용했던 두 가지 방법보다 빨랐다.

 

 

🔎 char을 잘 활용하자

사실 이전까지 char 배열은 잘 사용하지 않았다. 어떻게 보면 char 자료형 자체를 많이 안 썼던 거 같다. 하지만 레벨 1 문제를 74문제나 푼 나의 생각은 바꼈다. char[]를 잘 사용하면...엄청난 결과를 얻을 것이니..

 

# char[]은 문자열 치환이 가능하다

char[] ch = new char[]{'안', '녕', '하', '세', '요'};

String str1 = new String(ch); // "안녕하세요"
String str2 = String.valueOf(ch); // "안녕하세요"

이 기능은 필자는 문제 풀 때 정말 많이 사용했던 기능이다. 쓰이는 곳이 정말 많고, 유용하다. char[]을 문자열 생성자에 넣어주면 그대로 문자열을 반환해 준다니... 흥미롭지 않은가...? 나만 흥미로울 수도 있다. 👀

 

 

# char는 숫자로도 쉽게 사용이 가능하다

char[] ch = new char[]{'0', '1', '2', '3', '4', '5'};

int zero = ch[0] - '0';  // 숫자 0
int one = ch[1] - '0';   // 숫자 1
int two = ch[2] - '0';   // 숫자 2
int three = ch[3] - '0'; // 숫자 3

아스키 코드를 사용한 방법인데, '0'의 아스키코드 번호는 48이고, '1'은 49 '2'는 50 이렇게 증가한다. 즉 문자인 '1'에서 숫자 1을 얻기 위해서는 '1' - '0'을 해주면 된다. 49 - 48 이기 때문에 가능한 것이다. 

 

그냥 Integer.parseInt()나 Integer.valueOf()를 쓰면 되는 거 아닌가라고 생각하는 사람이 있으면 큰일이다. 한번 넣어보면 결과를 알 수 있을 것이다.

int zero = Integer.valueOf('0');
System.out.println(zero);

위에 있는 코드의 출력 결과가 뭐가 나올까? 0이 아닌 '0'의 아스키코드 번호인 48이 나온다. 그럼 parseInt()는? 이건 "java: incompatible types: char cannot be converted to java.lang.String" 이런 오류와 함께 컴파일 오류가 발생한다. parseInt()의 매개변수로 들어갈 수 있는 건 String 타입이기 때문이다. 그러니 아스키코드 활용을 잘 알아두면 좋을 것이다.

 

 

# 대문자와 소문자를 판별

char lowerValue = 'a';
char upperValue = 'A';

if (Character.isLowerCase(lowerValue)) {
    System.out.println("소문자 입니다."); // 출력
}

if (Character.isUpperCase(upperValue)) {
    System.out.println("대문자 입니다."); // 출력
}

Character에는 대문자와 소문자를 판별해주는 isLowerCase()와 isUpperCase() 메소드가 존재하기 때문에 이거도 잘 활용하면 코드를 대폭 줄일 수 있다. 숫자인지 판별하는 isDigit()도 있으니 잘 사용해 보기를 바랍니다.

 

 

🔎 코드를 줄이자!!! 스트림!!!

이라고 생각하면 큰일이다. Stream으로 해결한 몇몇 코드를 보면 정말 한 눈에 반할 정도로 멋지고, 나도 쓰고 싶어질 때가 있다. 심지어...한 줄로 해결해 버리는 문제도 많이 있었다.

 

하지만 스트림으로 푸는 문제들은 코드를 돌려보면 알겠지만 속도가 많이 느리다. 예를 들어 단순 반복문과 배열을 사용했을 때는 0.03ms에서 0.09ms가 나오는 반면에 스트림으로 해결한 문제를 돌려보면 1ms을 넘어가는 경우도 다반사이다. 아름다움에 현혹되서는 안 된다.

 

그렇다고 스트림이 좋지 않다라는 말은 아니지만 쓸 때와 쓰지 않을 때를 잘 구분해야 된다는 말이다. 알고리즘 문제를 풀 때는 이왕이면 스트림은 피하는 것이 좋다고 생각된다. 나같은 초짜는 언제 스트림을 쓰고 안 써야 하는지 구분을 못하기 때문에... 🙂

 

생각나는 부분은 일단 여기 까지이다. 이제는 레벨 2를 풀러 떠나야겠다. 

 

문제 상황

프로필 이미지를 업로드한 후 <img> 태그의 src로 해당 경로 요청했을 때, 한글이 깨지는 현상이 발생.

 

연두.png라는 사진 파일을 프로필 이미지로 업로드했고, DB에도 한글로 잘 데이터가 들어가 있는 것을 확인했다. 프로필에서 사용할 수 있는 DTO를 model에 담아서 jsp에 전달해 주는 Controller 로직을 작성했다.

// UserController.java

@GetMapping("/user/{pageUserId}")
public String profile(@PathVariable int pageUserId, Model model, @AuthenticationPrincipal PrincipalUserDetails principal) {
    UserProfileDto dto = userService.UserProfile(pageUserId, principal.getUser().getId());
    model.addAttribute("dto", dto);
    return "user/profile";
}

dto에 담겨있는 데이터 중에서 필자는 dto.user.profileImageUrl을 JSP에서 사용하고자 한다. 

// profile.jsp
<img class="profile-image" src="/upload/${dto.user.profileImageUrl}" onerror="this.src='/images/person.jpeg'" id="userProfileImage" />

위처럼 <img>의 src 요청을 보냈더니 콘솔에서 오류가 발생했다. 오류 내용은 아래와 같다.

요청을 원했던 URL
http://localhost:8080/upload/{UUID}_연두.png

실제로 요청된 URL
http://localhost:8080/upload/{UUID}_%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE.png

찾아보니 실제로 요청된 URL은 "연두"라는 한글을 UTF-8 방식으로 인코딩한 후 요청을 보낸 것으로 확인했다. 그럼 내가 원하는 정보는 실제로 요청된 URL에서 "%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE" 이 부분을 다시 UTF-8로 디코딩해서 "연두"로 요청해야 하는 건가...?

 

 

해결 사항

근본적인 해결 사항은 아니기 때문에 설정을 건드는 부분은 구글링을 통해서 찾아보시기 바랍니다. 웬만한 키워드로는 다 검색해 본 거 같습니다. "jsp img src 한글 깨짐", "jsp url encoding setting"과 같은 키워드를 통해서 StackOverFlow도 찾아보고 했지만, 대부분 Tomcat 설정 파일을 건드는 방식을 설명했던 거 같아서 아직 시도해보지는 못했습니다. jstl을 이용한 태그도 대부분 사용해 봤지만 통하지 않았습니다.

 

저는 이렇게 생각을 전환해 봤습니다. "http://localhost:8080/upload/{UUID}_연두.png" 이렇게 요청이 들어가면 한글 부분은 인코딩 되어서 "http://localhost:8080/upload/{UUID}_%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE.png" 실제로는 이렇게 요청이 간다.

 

그럼 애초에 사진 파일을 저장할 때 UTF-8로 파일명을 인코딩해서 업로드를 진행하면 되지 않을까? 예를 들어 "연두.png"라는 파일을 업로드하고 싶다면 인코딩을 해서 "%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE.png"로 사진 파일을 저장한 후에 DB에도 해당 인코딩된 정보를 저장합니다.

 

그리고 "http://localhost:8080/upload/{UUID}_%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE.png" 에 요청을 해버리면 되지 않을까?라는 생각으로 코드를 작성해 봤습니다.

 

가장 먼저 프로필 사진을 저장하는 로직을 가지고 있는 Service 로직을 변경했습니다.

// UserService.java
@Transactional
public User profileImageUrlUpdate(int principalId, MultipartFile profileImageFile) {
    UUID uuid = UUID.randomUUID();
    String originalName = profileImageFile.getOriginalFilename();
    String imageFileName = uuid + "_" + originalName;
    
    ...
}

기존 로직은 위와 같습니다. 사진 파일명이 동일한 문제를 발생시키지 않기 위해서 UUID를 생성해 줬고, 실제 파일의 이름을 받아왔습니다. "연두.png"라면 originalName은 "연두.png"가 될 것입니다. 그러고 나서 imageFileName에 UUID와 originalName을 언더바(_)로 구분하여 저장했습니다.

 

최종적으로 폴더에 저장되는 이름은 {UUID}_연두.png 입니다. 여기서 originalName 밑에 UTF-8 방식으로 인코딩하는 코드를 더 추가해 줬습니다.

import java.net.URLEncoder;

@Transactional
public User profileImageUrlUpdate(int principalId, MultipartFile profileImageFile) {
    UUID uuid = UUID.randomUUID();
    String originalName = profileImageFile.getOriginalFilename();
    try {
        originalName = URLEncoder.encode(originalName, "UTF-8");
    } catch (UnsupportedEncodingException exception) {
        throw new CustomApiException(exception.getMessage());
    }
    String imageFileName = uuid + "_" + originalName;
    
    ...
}

URLEncoder를 사용해 "연두.png"를 "%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE.png"으로 인코딩하여 저장했습니다. 아래는 폴더에 업로드된 사진 파일입니다.

{UUID}_%E1%84%8B%E1%85%A7%E1%86%AB%E1%84%83%E1%85%AE.png 으로 저장된 사진 파일

이제 기존 JSP 코드 그대로 요청을 해봅시다.

프로필 사진이 변경된 모습

원하는 이미지 파일이 잘 가져와지는 것을 볼 수 있습니다. 원래는 JSP와 SpringBoot의 원리를 바탕으로 한 번 해결법을 찾아보려 했지만 로직을 변경해서도 해결할 수 있을 거 같다는 생각에 한 번 도전해 봤습니다. 

 

물론 효율적이고 좋은 방법은 아닐지 모르지만 많은 자료를 구글링 해보는 과정에서 조금이라도 배움을 얻었다고 생각합니다. 구글링 하면서 찾은 사이트인데 원하는 방식으로 직접 인코딩, 디코딩해볼 수 있는 사이트를 아래 달아두겠습니다. 사용하기 괜찮은 거 같습니다!

 

https://www.urlencoder.org/

 

URL Encode and Decode - Online

Encode to URL-encoded format or decode from it with various advanced options. Our site has an easy to use online tool to convert your data.

www.urlencoder.org

 

간단한 문법의 활용이긴 하지만, 사용할 때마다 헷갈려서 기록해두려고 한다. 

 

1. Integer[] 배열을 List<Integer>로 변환

Integer[] 배열을 만들어서 List<Integer>로 만드는 방법은 매우 간단하다.

// 배열
Integer[] array1 = new Integer[]{1, 2, 3, 4, 5, 6, 7};

// 변환한 리스트
List<Integer> list1 = Arrays.asList(array1);

이는 Integer는 오브젝트이기 때문에 별다른 중간 작업이 필요없이 Arrays.asList()로만으로 변환이 가능하다.

 

 

2. int[] 배열을 List<Integer>로 변환

위에 Integer와는 다르게 int는 오브젝트가 아닌 기본형 타입이기 때문에 위와 같이 코드를 작성하면 어떻게 될까?

java: incompatible types: inference variable T has incompatible bounds
    equality constraints: java.lang.Integer
    lower bounds: int[]

위와 같은 컴파일 에러가 발생한다. List는 제너릭을 사용해서 Integer라는 Wrapper 객체를 받아야 하는데, int[]는 기본형 타입이기 때문이다. 그럼 어떻게 해줘야할까? 자바의 Stream을 사용하면 int[] 배열의 모든 인자를 Wrapper 클래스로 바꿔준 후 List로 변환할 수가 있다. 한번 코드로 작성해보자.

// 배열
int[] array1 = new int[]{1, 2, 3, 4, 5, 6, 7};

// 변환한 리스트
List<Integer> list1 = Arrays.stream(array1).boxed().collect(Collectors.toList());

위 코드를 해석해보면 먼저, array1 배열을 stream으로 만든 후에 boxed() 메소드를 적용하면 int 기본형 타입을 모두 Wrapper 클래스로 변환시켜 줍니다. 그 후 collect()를 사용해서 List로 변환시켜 줍니다. 

 

 

3. List<Integer> 리스트를 int[] 배열로 변환

위에서 기본형 타입인 int 배열을 Stream을 사용해서 List<Integer>로 변환해봤다. 이제 반대로는 어떻게 변환할 수 있을까? 반대로 변환할 때도 Stream을 써볼 것이다.

// 리스트
List<Integer> list1 = List.of(1, 2, 3, 4, 5, 6, 7);

// 변환한 배열
int[] array1 = exList.stream().mapToInt(Integer::intValue).toArray();

새로운 메소드가 나왔다. mapToInt() 이다. 해당 메소드를 위와 같이 mapToInte(Integer::intValue)로 사용하면 리스트에 있는 Wrapper 타입의 인자들을 하나씩 돌면서 기본형 타입인 int로 변환해줍니다. 그리고 마무리로 toArray() 메소드를 통해서 배열로 변환할 수 있습니다.

 

생각보다 int[]를 List<Integer>로 변환하는 경우가 좀 있었던 거 같아서 이렇게 기록해두려고 했다. 이제는 안 까먹어야지. 😄

🙅🏻‍♂️ 오류발생

Springboot와 JSP를 사용하는 과정에서 외부 리소스(로컬에 저장되어있는 이미지 파일)를 JSP의 <img> 태그의 src로 사용하려고 했는데, 경로를 잘 지정해줘도 x박스가 계속 뜨는 문제에 직면

 

기존에 사용하던 방법은 다음과 같습니다. application.yml에서 file에 대한 경로를 커스텀하기 위한 설정을 먼저 진행했습니다.

// application.yml

file:
  path: /User/username/IdealProjects/imageStorage

/User/username/IdealProjects/imageStorage 라는 경로에 있는 폴더에 이미지 파일을 저장하기 위해서 커스텀해놓은 것이다. 이렇게 application.yml에 넣어두면 @Value("${file.path}")로 어디서든 가져다 쓸 수 있다.

 

// Image.java

@Entity
public class Image {
    ...
    
    private String imageName;
    
    ...
}

내가 만들었던 Image 클래스에는 사진 파일이름을 저장할 수 있는 imageName이라는 필드가 존재한다. 예를 들어 사진 파일이름이 A.jpg라고 하면, 위에 설정된 경로인 /User/username/IdealProjects/imageStorage에 A.jpg를 저장한다.

 

이제 이 Image를 사용하고자하는 photo.jsp에서 사용하기 위한 Controller 작업을 해줬습니다.

// ImageController.java

@Controller
public class ImageController {
    @GetMapping("/photo/{id}")
    public String photo(@PathVariable int id, Model model) {
        Image img = imageRepository.findById(id).orElseThrow(() -> {
            throw new CustomException("찾으려는 파일은 존재하지 않습니다.");
        });
        
        model.addAttribute("image", img);
        
        return "photo";
    }
}

가져 오려는 사진을 Repository에서 찾아서 Model에 넣어서 photo.jsp에서 사용할 수 있게 전달해줬습니다.

 

이제 photo.jsp에서 /User/username/IdealProjects/imageStorage/{이미지 파일 이름} 이라는 경로를 src에 넣어주면 된다. 하지만 이미지 파일을 가져올 때 사용되는 앞 부분 경로는 우리가 이미 앞에 application.yml에 설정해놨기 때문에 이를 스프링에서 사용할 수 있도록 WebConfig를 사용해줄 수 있다.

 

// WebMvcConfig.java

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Value("${file.path}")
    private String filePath;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        WebMvcConfigurer.super.addResourceHandlers(registry);

        registry
                .addResourceHandler("/image/**")
                .addResourceLocations("file:///" + filePath);

    }
}

WebMvcConfigurer을 상속받으면 스프링 Web 설정파일로서 작동이 되고, @Configuration을 적어주면 IoC 컨테이너에 빈으로 등록해놓을 수 있다. 

 

이제 @Value("${file.path}")를 사용해서 application.yml에 있는 커스텀 파일 경로의 값을 filePath에 넣어준다. 이제 addResourceHandlers()를 오버라이딩하고, addResourceHandler("/image/**")를 써서 /image/ 경로로 설정된 요청이 온다면, addResourceLocations("file:///" + filePath)를 써서 "file:///User/username/IdealProjects/imageStorage"로 바꿔서 작동된다는 설정이다.

 

이제 설정은 끝났으니 photo.jsp에서 이미지 파일을 사용해보자.

// photo.jsp
...

<img src="/image/${image.imageName}" />

...

로컬에 있는 파일을 가져오기 위해서 위와 같이 src 경로를 넣어줬습니다. 근데 사진은 x 박스 쳐지면서 가져오질 리소스를 가져오지 못했다. 찾아보니 보안상으로 웹 브라우저에서 로컬 리소스에 Get 요청을 할 수 없다는 거 같다는 내용이 많이 보였다. 자세한 원인을 찾고 싶었는데 아직 찾지 못했다... 만약 어쩌다 이 글을 읽게되시는 고수분이 있으면 알려주셨으면 좋겠다.

 

 

 

🙆🏻‍♂️ 문제해결

jsp에서 외부 리소스를 사용하기 위해서 구글링하다 해답을 찾았다. 스프링 태그 라이브러리(tglibs)를 사용하는 것이다. 먼저 jsp의 Header에 다음 코드를 추가한다.

// photo.jsp

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>

...

spring이라는 prefix로 해당 기능을 사용할 수 있게되었다. 이제 이 <spring:>태그로 위에서 설정해줬던 url을 수정해볼 것이다.

 

// photo.jsp
...

<img src="<spring:url value='/image/${image.imageName}' />" />

...

이렇게 <spring:url />로 Url을 재설정해주니 사진이 정상적으로 가져와지는 것을 확인할 수 있었다.

🙅🏻‍♂️ 오류 발생

지금까지 DB를 쓸 때는 항상 MySQL을 사용했었다. MySQL은 Homebrew를 통해서 다운로드 했고, 새로운 프로젝트 연습을 위해서 MariaDB를 사용해보려고 했다. 그래서 MariaDB를 다운로드 받는 방법을 따라서 다운로드 했고, 다운로드 완료 후 MariaDB 실행 과정에서 해당 오류가 발생했다.

ERROR 2002 (HY000): Can't connect to local server through socket '/tmp/mysql.sock'

예전에 내 노트북은 아니고, 친구 노트북에서 해당 오류를 접했던적이 있었다. /tmp/mysql.sock 문제는 원인이 매우 많다고 한다. 그때는 뭔가 이것저것 다 건들다가 결국 안돼서 노트북을 그냥 밀어버렸던 기억이 있다. 

 

 

 

🙆🏻‍♂️ 해결과정

내가 해결했던 과정을 기록해보려고 한다. 발생 원인에 따라서 다를 수도 있으니 한번 글을 모두 읽고나서 따라해보는 것을 추천한다. 따라갔다가 돌아올 수 없는 길을 건널 수도 있으니...

 

 

1. MySQL 삭제

일단 생각했던 건 MySQL과 MariaDB가 둘다 같은 포트번호인 3306 포트를 사용한다고 한다. 그러니깐 MySQL과 MariaDB를 모두 삭제하고 다시 깔아보자는 생각을 했다. 아래에 기록하는 과정은 Homebrew를 통해서 다운로드한 경우에만 가능합니다.

# MySQL이 brew services에서 실행 중이라면
> brew services stop mysql
# homebrew에서 mysql 삭제
> brew uninstall mysql

 

 

2. MariaDB 삭제

# MariaDb가 brew services에서 실행 중이라면
> brew services stop mariadb
# homebrew에서 mariaDB 삭제
> brew uninstall mariadb
> brew cleanup

 

 

3. MySQL, MariaDB와 관련된 파일 모두 삭제

여러 게시글에서 finder에 직접 mysql, mariadb를 검색해서 찾아서 삭제하라고 하는데, 제 노트북에는 많은 프로젝트 파일들이 있어서 해당 파일에 mysql 관련된 파일이 너무 많아서 분류할 수 없었습니다. 그래서 경로로 타고 들어가서 직접 디렉터리와 파일을 삭제했습니다.

 

m1칩과 intel 칩 Mac book이 homebrew 사용 시 다운로드되는 경로가 다르다고 알고있습니다. 저는 m1칩 기준으로 설명했기 때문에 경로는 구글링으로 자세하게 찾아본 후 해보시기 바랍니다.

 

# my.cnf 관련된 파일 모두 삭제하기

 

/opt/homebrew/etc

 

위 폴더의 경로는 다음과 같다. "/opt/homebrew/etc" 터미널이 편하다면 해당 경로로 가서 커멘드로 삭제해줘도 된다. 근데 세상에 마우스로 누르는거보다 커멘드가 편한 사람이 몇이나 있을까 🥲

 

이제 관련된 파일이라는 것은 my.cnf가 포함된 파일, 폴더는 다 삭제해주면 된다. 예를 들어 위 폴더에는 "my.cnf", "my.cnf.d", "my.cnf.default"가 있다. 제 폴더에 있는 파일과 다를 수도 있으니 my.cnf가 들어가있는 파일, 폴더를 모두 삭제해줍니다.

 

맥북에서 해당 경로로 이동하는건 생각보다 매우 쉽다. finder로 들어가서 왼쪽 위 상단에 "이동"이라는 메뉴가 있다. 누른 후 "폴더로 이동" 누르면 경로로 이동할 수 있다.

finder > 이동 > 폴더로 이동

 

# mySQL 관련 폴더 삭제하기

위에서 my.cnf 관련 파일을 모두 삭제했다면 다음 경로로 이동해줍니다. "/opt/homebrew/var" 이동해보면 mysql이라는 파일이 있는 것을 볼 수 있습니다. 음....나 mysql 삭제했는데 왜 아직 폴더가 남아있지..?라는 생각이 들겁니다.

/opt/homebrew/var

 

여기에 있는 mysql 폴더도 삭제해줍니다.

 

 

4. MiriaDB 설치하기

이제 Homebrew를 통해서 MariaDB를 설치해줍니다. 

# MariaDB 설치
> brew install mariadb
# MariaDB 설치 확인하기
> mariadb -V

설치 완료 후에 버전을 확인해보면 본인이 다운로드 받은 MariaDB의 버전을 확인할 수 있습니다. 저 같은 경우는 아래와 같이 뜹니다.

mariadb  Ver 15.1 Distrib 10.11.2-MariaDB, for osx10.18 (arm64) using  EditLine wrapper

 

이제 MariaDB가 설치되었으니 실행시켜 봅시다.

# Homebrew에서 MariaDB 서비스 시작
> brew services start mariadb
# Homebrew에서 실행 중인 서비스 확인하기
> brew services

brew services 결과 아래와 같이 뜨면 성공입니다!

Name    Status  User       File
mariadb started {본인의 맥북이름} ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist

기존에는 Status가 stopped 상태였는데 이제, started가 되었습니다! 이제 MariaDB에 접근이 되는지 확인해보면 끝입니다. 터미널에 아래와 같이 입력합니다.

# MariaDB 실행
> mariadb

실행되면 아래와 같이 터미널이 변합니다.

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 4
Server version: 10.11.2-MariaDB Homebrew

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

이제 MariaDB가 정상적으로 실행됐습니다. 여기까지 확인했으면 이제 MariaDB 실행을 종료할 때는 exit라고 쳐주면 Bye 라는 문구가 나오면서 MariaDB가 종료됩니다.

최근 스프링 부트 학습을 하는데 맨날 gradle로 빌드를 하다가 최근에 빌드 툴을 maven으로 하고 프로젝트를 생성했다. 초기 프로젝트 세팅은 아래와 같이 진행했다.

 

https://start.spring.io/

 

필요할 거 같은 라이브러리를 모두 추가해줬다. 압축파일을 풀고 인텔리제이에서 간단한 RestController를 하나 작성하고, 프로젝트를 실행시켜봤다. 근데 계속 아래와 같은 로그인하라는 페이지가 랜더링되었다. 난 저런 페이지를 추가한적이 없는데?

 

프로젝트 실행 시 8080 포트 들어가면 제일 먼저 뜨는 화면

 

뭘 로그인하라는거지라는 생각으로 구글링을 해봤고, Spring Security 라이브러리를 의존성 추가해놨기 때문에 뜨는 화면이라고 한다. 근데 지금은 사용하지 않을거 같아서 maven 초기 빌드를 진행하고, Spring Security에 관련된 코드는 모두 주석 처리 해놨다. 근데도 계속 Please Sign in 페이지가 뜬다. 어떻게 해결할 수 있을까

 

 

🔎 (방법 1) 로그에 출력되는 password를 사용해서 로그인

프로젝트를 실행하고 인텔리제이 로그를 확인해보면 password가 하나 만들어지는 것을 확인할 수 있다. Username에 user를 입력하고, Password에 로그에 출력되어있는 비밀번호를 입력해서 로그인하면 해결된다. 

 

근데, 프로젝트를 종료하고 다시 실행하면 계속 같은 화면이 뜬다. 계속 이렇게 로그인하는게 너무 귀찮아서 다른 방법이 있나 하고 계속 찾아봤다. 코드 상에서 Spring Security 기능을 비활성화해두는 코드를 하나 찾았다.

 

 

🔎 (방법 2) Spring Security 기능 비활성화 해두기

// Application.java

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class BlogApplication {
	public static void main(String[] args) {
		SpringApplication.run(BlogApplication.class, args);
	}

}

main 메서드가 존재하는 클래스에 들어가서 @SpringBootApplication에 위와 같이 exclude 값을 넣어주면 된다. Spring Security를 사용하지 않을 때, 비활성화 해둘 수 있는 코드라고 한다.

 

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)

이렇게 설정해주고 다시 프로젝트를 실행시켜보니 이제 Please Sign in 페이지가 안 뜨는 것을 확인할 수 있다. 후 속이 편안해졌다. 계속 로그인하느냐고 힘들었는데...

+ Recent posts