이번 주에는 마이 페이지에서 사용자의 프로필 사진을 업데이트하는 기능을 개발하게 되었습니다. 이미지를 저장하기 위해 여러 방법을 고려했는데, 그중 로컬 서버 대신 안정적이고 확장성이 높은 클라우드 스토리지인 AWS S3를 선택하게 되었습니다. AWS S3를 사용하는 방법에 대한 글은 많이 찾아볼 수 있지만, 이번 글에서는 S3를 사용하면서 마주쳤던 문제들과 그 해결 과정에 대해 회고해보려 합니다.

S3 접근 권한 설정

S3 버킷에 파일을 업로드하려면 먼저 해당 버킷에 접근할 수 있는 권한이 필요합니다. 이때 엑세스 키시크릿 키라는 두 개의 키가 사용됩니다. 키는 AWS에서 직접 발급할 수 있지만, 보안상 매우 중요한 문제입니다. 만약 키가 외부로 노출된다면, 누군가가 이를 악용해 우리가 모르는 사이 비트코인 채굴 등의 작업을 수행하거나, 막대한 서버 비용을 초래할 수 있습니다.

따라서 절대로 AWS Root 계정으로 발급된 키를 사용해서는 안 됩니다. 대신, S3에만 접근할 수 있는 최소한의 권한을 가진 사용자를 별도로 만들어 그 키를 사용하는 것이 안전합니다. 이를 위해 AWS의 Identity and Access Management(IAM)에서 사용자 그룹을 생성하고, 해당 그룹에 AmazonS3FullAccess 권한을 할당합니다. 이후, 그 그룹에 속한 사용자에게만 엑세스 키를 발급하면, S3 버킷에 대한 안전한 접근이 가능합니다.

IAM에서 권한을 부여하고 키를 발급하는 과정은 다음과 같습니다:

  1. AWS 관리 콘솔에서 IAM 대시보드로 이동합니다.
  2. 사용자 그룹을 생성하고, AmazonS3FullAccess 권한을 해당 그룹에 할당합니다.
  3. 해당 그룹에서 사용자를 생성하고, 그 사용자에게 엑세스 키를 발급합니다.

이제, 이 엑세스 키와 시크릿 키를 사용하여 S3에 접근할 수 있게 됩니다.

GitHub에서의 보안 관리

S3 접근 키를 발급받았다면, 이를 서버에서 안전하게 사용할 방법을 고민해야 합니다. 특히 GitHub에 소스 코드를 올릴 때는 더욱 주의가 필요합니다. 퍼블릭 레포지토리에 키가 노출되면 외부에서 악용될 가능성이 높습니다. 이를 방지하기 위해 GitHub의 Secrets 기능을 활용해 환경 변수를 설정할 수 있습니다. 이렇게 하면 키가 소스 코드에 포함되지 않고도 안전하게 사용할 수 있습니다.

로컬 환경에서는 application.yml 파일을 사용해 키를 관리하는 것이 일반적입니다. 그러나 application.yml 파일이 git ignore에 등록되지 않으면, 실수로 이 파일이 퍼블릭 레포지토리에 노출될 수 있습니다. 이를 방지하기 위해, application-local.yml, application-dev.yml, application-prod.yml처럼 환경별로 파일을 분리하고, application-prod.yml만 실제 서버에 배포하도록 설정하는 것이 좋습니다. 이 방법은 Spring Boot를 사용하는 개발자들에게 특히 유용합니다.

S3 파일 업로드 기능 구현

이제 S3 버킷에 접근하기 위한 설정을 마쳤으니, 본격적으로 파일을 업로드하는 기능을 구현해보겠습니다. 우선, 필요한 의존성을 추가해야 합니다.

Gradle 의존성 추가

S3 파일 업로드를 위해 AWS SDK를 의존성에 추가해야 합니다. Kotlin과 Spring Boot 환경에서 다음과 같이 의존성을 추가할 수 있습니다.

implementation("org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE")​
 

application.yml 설정

application.yml 파일에 S3와 관련된 설정 정보를 입력합니다. 여기에서 stack.auto를 false로 설정하여 Spring Boot가 AWS 인프라를 자동으로 구성하지 않도록 해야 합니다. 또한, region.static 값을 ap-northeast-2로 설정하여 S3 버킷의 리전을 서울로 지정합니다.

cloud:
  aws:
    s3:
      bucket: 본인의 버킷 명
    stack.auto: false
    region.static: ap-northeast-2
    credentials:
      accessKey: 본인의 엑세스 키
      secretKey: 본인의 시크릿 키

S3Config 설정 클래스

S3에 접근하기 위한 클라이언트를 설정하는 S3Config 클래스를 작성합니다. 이 클래스는 S3 클라이언트를 Spring 애플리케이션에서 빈으로 등록하여 다른 곳에서 재사용할 수 있게 해줍니다.

@Configuration
class S3Config(
        @Value("\${cloud.aws.credentials.access-key}") private val accessKey: String,
        @Value("\${cloud.aws.credentials.secret-key}") private val secretKey: String,
        @Value("\${cloud.aws.region.static}") private val region: String
) {

    @Bean
    fun amazonS3Client(): AmazonS3Client {
        val awsCredentials = BasicAWSCredentials(accessKey, secretKey)
        return AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(AWSStaticCredentialsProvider(awsCredentials))
                .build() as AmazonS3Client
    }
}

파일 업로드 서비스

S3 버킷에 파일을 업로드하기 위한 서비스 클래스를 작성합니다. 이 클래스에서는 파일을 업로드하고, 업로드된 파일의 URL을 반환하는 기능을 구현합니다.

@Service
class S3ImageUploadService(
        private val amazonS3Client: AmazonS3
) {

    @Value("\${cloud.aws.s3.bucket}")
    private lateinit var bucket: String

    @Throws(IOException::class)
    fun uploadFile(file: MultipartFile): String {
        val fileName = generateFileName(file.originalFilename!!)
        val metadata = ObjectMetadata()
        metadata.contentLength = file.size

        amazonS3Client.putObject(bucket, fileName, file.inputStream, metadata)
        return amazonS3Client.getUrl(bucket, fileName).toString() // 업로드된 파일의 URL 반환
    }

    private fun generateFileName(originalFilename: String): String {
        val fileExtension = originalFilename.substringAfterLast(".")
        return "${UUID.randomUUID()}.$fileExtension"
    }
}

마무리

이 글을 통해 AWS S3를 사용하여 이미지를 업로드하는 방법과, 그 과정에서 발생할 수 있는 보안 문제에 대해 다루었습니다. 특히 IAM 권한 관리GitHub 보안 설정에 대한 부분은 꼭 주의해야 할 사항입니다. 이를 통해 AWS S3를 효율적이고 안전하게 사용할 수 있기를 바랍니다.

'스프링' 카테고리의 다른 글

@Entity  (0) 2022.02.01
spring boot 에서 google login을 사용해보았다.  (0) 2021.12.18
Spring 웹 계층  (0) 2021.12.14
스프링 서버 단에서 데이터 처리하는 방식  (0) 2021.11.27
Auditing @CreatedDate @LastModifiedDate  (0) 2021.11.27

@Entity

엔티티 클래스 애노테이션

데이터베이스에 저장(persist)할 자바 객체를 정의

- 다양한 애노테이션을 이용해 보다 자세한 테이블 스키마 정보를 표현

- 애노테이션으로 표현한 스키마 정보와 실제 테이블 스키마가 완벽히 일치해야 할 필요는 없음

- 하나의 도메인(domain)으로 간주

 

@Entity 클래스 안에서 사용되는 주요 JPA 애노테이션

- @Table, @Index, @UniqueConstraint: 테이블 기본 정보와 인덱스, unique 키를 설정

- @Id, @GeneratedValue: primary key 설정

- @Column: 각 컬럼 설정

- @Enumerated: enum 을 처리하는 방법을 설정

- @Transient: 특정 필드를 DB 영속 대상에서 제외

- @OneToOne, @OneToMany, @ManyToOne, @ManyToMany: 연관 관계 설정

- @MappedSuperClass: 상속을 이용한 공통 필드 정의

- @Embedded, @Embeddable: 클래스 멤버를 이용한 공통 필드 정의

- @DataTimeFormat: 스프링에서 제공하는 애노테이션, 날짜 입력의 포맷을 지정

 

 

@Entity: JPA엔티티의 lifecycle event를 활용한 Auditing(생성자, 생성일자, 수정자, 수정일자) 테크닉

JPA엔티티에 생성일시, 수정일시 같이 일정하게 작성하는 메타데이터를 처리 가능

- @PrePersist

- @PostPersist

- @PreRemove

- @PostRemove

- @PreUpdate

- @PostUpdate

- @PostLoad

 

@Entity: Spring JPA Auditing 애노테이션

엔티티의 생성일시, 수정일시, 생성자, 수정자를 자동으로 관리해주는 애노테이션

설정

- @EnableJpaAuditing

- @EntityListeners(AuditingEntityListener.class)

활용

- @CreatedBy

- @CreatedDate

- @LastModifiedBy

- @LastModifiedDate

 

로그인 기능을 사용하기 위해서 oauth2 를 임포트 합니다.

compile('org.springframework.boot:spring-boot-starter-oauth2-client')

깃 이그노어 파일에 아래와같이 인증 키 정보가 담긴 application-oauth 파일을 제거합니다.

인증 키의 노출은 보안에 위험하기 때문입니다.

application-oauth.properties

application.properties에서 oauth 사용을 허락합니다.

spring.profiles.include=oauth

index.mutache에 아래에 해당되는 파일을 추가합니다. 

                {{#userName}}
                    Logged in as: <span id="user">{{userName}}</span>
                    <a href="/logout" class="btn btn-info active" role="button">Logout</a>
                {{/userName}}
                {{^userName}}
                    <a href="/oauth2/authorization/google"
                       class="btn btn-success active" role="button">Google Login</a>
                {{/userName}}

{{}} 문법은 머스테치 문법입니다. 컨트롤러에서 index.mustache 파일을 호출할때 userName 정보를 index.mustache파일에 넘겨주게 되면 로그인한 유저 정보와 logout 버튼을 나타내고, userName이 넘어오지않게 된다면 {{^userName}}이 실행되게 됩니다. ^표식이 들어가면 없을때 안의 값을 반환합니다.

 

 

package com.bell_bell.book.springboot.config.auth;

import com.bell_bell.book.springboot.config.auth.dto.OAuthAttributes;
import com.bell_bell.book.springboot.config.auth.dto.SessionUser;
import com.bell_bell.book.springboot.domain.user.User;
import com.bell_bell.book.springboot.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.Collections;

@RequiredArgsConstructor
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    private final UserRepository userRepository;
    private final HttpSession httpSession;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
        OAuth2User oAuth2User = delegate.loadUser(userRequest);

        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails()
                .getUserInfoEndpoint().getUserNameAttributeName();

        OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());
        User user = saveOrUpdate(attributes);
        httpSession.setAttribute("user", new SessionUser(user));

        return new DefaultOAuth2User(
                Collections.singleton(new SimpleGrantedAuthority(user.getRoleKey())),
                attributes.getAttributes(),
                attributes.getNameAttributeKey());

    }


    private User saveOrUpdate(OAuthAttributes attributes) {
        User user = userRepository.findByEmail(attributes.getEmail())
                .map(entity -> entity.update(attributes.getName(), attributes.getPicture()))
                .orElse(attributes.toEntity());

        return userRepository.save(user);
    }
}

위 클래스 CustomOAuth2UserService에는 loadUser와 saveOrUpdate 메소드가 존재합니다.

loadUser 메소드는 구글 로그인 이후 호출되게 됩니다. 해당 클래스는SecurityConfig 에서 호출합니다. loadUser가 호출되면 loadUser 내부에는 saveOrUpdate가 실행되게 됩니다. userRequest에는 유저 이름과 아이디에 관한 정보가 포함되 있는 것으로 보이며 해당 정보를 saveOrUpdate 함수에게 전달합니다. saveOrUpdate 는 userRepository에게 해당 유저가 존재하는지 여부를 물어보고 존재 유무는 해당 유저의 이메일로 유저를 찾게 됩니다. orElse의 기능으로 인해서 해당 유저가 없다면 attributes.toEntity() 호출로 인해서 유저 클래스에 값을 넘기게 되고 해당 유저는 save 되는 것으로 보입니다. 

 

그냥 간단하게 요약하자면 구글 로그인이 진행되면 해당 클래스에 있는 메소드를 통해서 httpSession에 유저 정보를 저장하고 User table에 유저 정보를 저장하는 거로 보면 됩니다.

 

import com.bell_bell.book.springboot.domain.user.Role;
import com.bell_bell.book.springboot.domain.user.User;
import lombok.Builder;
import lombok.Getter;

import java.util.Map;

@Getter
public class OAuthAttributes {
    private Map<String, Object> attributes;
    private String nameAttributeKey;
    private String name;
    private String email;
    private String picture;

    @Builder
    public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name,
                           String email, String picture) {
        this.attributes = attributes;
        this.nameAttributeKey = nameAttributeKey;
        this.name = name;
        this.email = email;
        this.picture = picture;
    }

    public static OAuthAttributes of(String registrationId, String userNameAttributeName,
                                     Map<String, Object> attributes) {
        return ofGoogle(userNameAttributeName, attributes);
    }

    private static OAuthAttributes ofGoogle(String userNameAttributeName, Map<String, Object> attributes) {
        return OAuthAttributes.builder()
                .name((String) attributes.get("name"))
                .email((String) attributes.get("email"))
                .picture((String) attributes.get("Picture"))
                .attributes(attributes)
                .nameAttributeKey(userNameAttributeName)
                .build();
    }

    public User toEntity() {
        return User.builder()
                .name(name)
                .email(email)
                .picture(picture)
                .role(Role.GUEST)
                .build();
    }
}

위 클래스는 dto에 속하는 클래스로 각 계증간 유저 정보를 주고 받을 때 사용됩니다.

toEntity를 자세히 보면 .role(Role.GUEST)로 되어있습니다. 이는 가입할 때 기본 권한을 GUEST로 주기 위해서 role 빌더 값에는 Role.GEUSET를 사용합니다.

 

package com.bell_bell.book.springboot.config.auth.dto;

import com.bell_bell.book.springboot.domain.user.User;
import lombok.Getter;

import java.io.Serializable;

@Getter
public class SessionUser implements Serializable {
    private String name;
    private String email;
    private String picture;

    public SessionUser(User user) {
        this.name = user.getName();
        this.email = user.getEmail();
        this.picture = user.getPicture();
    }
}

위 클래스가 사용되는 곳을 살펴보겠습니다.

        httpSession.setAttribute("user", new SessionUser(user));

위와 같이 사용되고 있고 httpSession에 유저 정보를 저장 시키게 하기 위해서 사용 되는 것으로 보입니다.

 

httpSession에 유저 정보를 저장할 때 왜 User 클래스 대신 SessionUser를 사용하는 걸까요? httpSession에 User를 담기위해서는 Serialize(직렬화)를 사용해야 되는데 User를 직렬화 하게 되면 나중에 자식 엔티티가 생겨서 관계를 가지게 되면 직렬화 대상에 자식까지 포함되어서 성능 이슈, 부수 효가가 발생할 수 있기 때문입니다. 

 

다음으로 아래 클래스 정보를 알아보겠습니다.

package com.bell_bell.book.springboot.config.auth;

import com.bell_bell.book.springboot.domain.user.Role;
import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final CustomOAuth2UserService customOAuth2UserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .headers().frameOptions().disable()
                .and()
                    .authorizeRequests()
                    .antMatchers("/", "/css/**", "/images/**",
                            "/js/**", "/h2-console/**").permitAll()
                    .antMatchers("/api/v1/**").hasRole(Role.USER.name())
                    .anyRequest().authenticated()
                .and()
                    .logout()
                        .logoutSuccessUrl("/")
                .and()
                    .oauth2Login()
                        .userInfoEndpoint()
                            .userService(customOAuth2UserService);
    }
}

1. @EnableWebSecurity

 - Spring Security 설정들을 활성화시켜 줍니다.

 

2. csrf().disable().headers().frameOptions().disable()

- h2-console 화면을 사용하기 위해 해당 옵션들을 disable 합니다. 

 

3. authorizeRequests

- URL별 권한 관리를 설정하는 옵션의 시작지점입니다. 

- authorizeRequests가 선언되어야만 antMatchers 옵션을 사용할 수 있습니다. 

 

4. andMachers

- url을 지정하고 해당되는 url에게 전체 열람부터 특정 권한을 할당합니다. 

- 권한 관리 대상을 지정하는 옵션입니다.

- URL, HTTP 메소드별로 관리가 가능합니다.

- "/"등 지정된 URL들은 permitAll()옵션을 통해 전체 열람 권한을 주었습니다.

- "/api/v1/**" 주소를 가진 api는 USER 권한을 가진 사람만 가능하도록 했습니다.

 

5. anyRequest

- 설정된 값들 이외 나머지 URL들을 나타냅니다. 

- 여기서는 authenticated()을 추가하여 나머지 URL들은 모두 인증된 사용자들에게 만 허용하게 합니다.

- 인증된 사용자 즉, 로그인한 사용자들을 이야기합니다.

 

6. logout().logoutSuccessUrl("/")

 - 로그아웃 기능에 대한 여러 설정의 진입점입니다.

 - 로그아웃 성공 시 / 주소로 이동합니다.

 

7. oauth2Login

 - OAuth 2 로그인 기능에 대한 여러 설정의 진입점입니다. 

 

8. userInfoEndpoint

 - OAuth2  로그인 성공 이후 사용자 정보를 가져올 때의 설정들을 담당합니다. 

 

9. userService

 - 소셜 로그인 성공 시 후속 조치를 진행할 UserService 인터페이스의 구현체를 등록합니다. 리소스 서버(즉, 소셜 서비스들)에서 사용자 정보를 가져온 상태에서 추가로 진행하고자 하는 기능을 명시할 수 있습니다. 

해당 내용은 스프링 부트와 AWS로 혼자 구현하는 웹서비스 책 내용을 복습한 내용입니다.

 

API를 만들기 위해 총 3개의 클래스가 필요합니다.

* Request 데이터를 받을 Dto

* API 요청을 받을 Controller

* 트랜잭션, 도메인 기능 간의 순서를 보장하는 Service

tip ) service는 비지니스 로직을 처리하지 않습니다. 그 역할은 Domain이 실행합니다.

 

* Web Layer

  * 흔히 사용하는 컨트롤러(@Controller)와 JSP/Freemaker 등의 뷰 템플릿 영역입니다.

  * 이외에도 필터(@Filter), 인터셉터, 컨트롤러 어드바이스(@ControllerAdvice) 등 외부 요청과 응답에 대한 전반적인 영역을 이야기합니다.

 

* Service Layer

  * @Service에 사용되는 서비스 영역입니다.

  * 일반적으로 Controller와 Dao의 중간 영역에서 사용됩니다.

  * @Transactional이 사용되어야 하는 영역이기도 합니다.

 

* Repository Layer

  * Database와 같이 데이터 저장소에 접근하는 영역입니다.

  * 기존에 개발하셨던 분들이라면 Dao(Data Access Object) 영역으로 이해하시면 쉬울것입니다.

 

* Dtos

  * Dto(Data Transfer Object)는 계층간 데이터를 교환하기 위한 객체를 이야기하며 Dtos는 이들의 영역을 얘기합니다.

  * 예를 들어 뷰 템플릿 엔진에서 사용될 객체나 Repository Layer에서 결과로 넘겨준 객체 등이 이들을 이야기합니다.

 

* Domain Model

  * 도메인이라 불리는 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화시킨 것을 도      메인 모델이라고 합니다.

 

  * 이를테면 택시 앱이라고 하면 배차, 탑승, 요금 등이 모두 도메인이 될 수 있습니다.

  * @Entity를 사용해보신 분들은 @Entity가 사용된 영역 역시 도메인 모델이라고 이해 해주시면 됩니다.

  * 다만, 무조건 데이터베이스의 테이블과 관계가 있어야만 하는 것은 아닙니다.

  * VO처럼 값 객체들도 이 영역에 해당하기 떄문입니다.

 

 

 

'스프링' 카테고리의 다른 글

@Entity  (0) 2022.02.01
spring boot 에서 google login을 사용해보았다.  (0) 2021.12.18
스프링 서버 단에서 데이터 처리하는 방식  (0) 2021.11.27
Auditing @CreatedDate @LastModifiedDate  (0) 2021.11.27
h2 console  (0) 2021.11.27

'스프링' 카테고리의 다른 글

spring boot 에서 google login을 사용해보았다.  (0) 2021.12.18
Spring 웹 계층  (0) 2021.12.14
Auditing @CreatedDate @LastModifiedDate  (0) 2021.11.27
h2 console  (0) 2021.11.27
form과 button  (0) 2021.11.21

위와 같이 특정 모델에 위 속성을 추가한 후

main 메소드가 있는 곳에 @EnableJPaAuditing 어노테이션을 추가해주면 행을 추가할 때마다 @CreatedDate 이 있는 속성 의 값이 자동으로 들어가고 @LastModifiedDate 어노테이션이 있는 속성에는 update시점의 시간을 자동으로 기록하게 된다.

'스프링' 카테고리의 다른 글

Spring 웹 계층  (0) 2021.12.14
스프링 서버 단에서 데이터 처리하는 방식  (0) 2021.11.27
h2 console  (0) 2021.11.27
form과 button  (0) 2021.11.21
스프링 프로젝트 base Url 설정  (1) 2021.11.12

h2의 경로를 콘솔로 확인하기 위해서 강의에서 처럼 아래와 같이 해보았지만 되지않았다.

강의에서는 파일 이름이 application.yml 이였지만 최근에는 application.properties로 바뀌면서 뭔가 바뀐 듯 하다. 

그래서 인터넷 검색을 통해 알게 되었는데 application.properties에서 아래와 같이 해보았다.

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

다행이 아래와 같이 경로를 확인 후 강의를 계속 들을 수 있었다.

 

<form method="post" action="/board/post">
	<button type="submit" name="register">등록</button>
</form>

코드를 짜면서 위와같이 form 안에 button 을 생성하여 사용해보았다.

재밌는 점은 버튼을 클릭하게 되면 button 의 name이 파라미터로 적용되서 url이

/board/post?register 와 같이 된다는 것이다.

'스프링' 카테고리의 다른 글

Auditing @CreatedDate @LastModifiedDate  (0) 2021.11.27
h2 console  (0) 2021.11.27
스프링 프로젝트 base Url 설정  (1) 2021.11.12
스프링 프로젝트 기본 설정하기  (0) 2021.11.12
이클립스 패키지 프리젠테이션  (0) 2021.11.10

 

진행중인 프로젝트를 우클릭 한 후 Properties를 선택한다.

 

Web Project Settings를 선택한 다음 Context root를 위와같이 /로 변경한다.

 

그다음에 톰캣 서버를 우클릭 후 clean을 눌러준다음 테스트하면

월래는 /mvc가 달려있었는데 사라진 것을 확인할 수 있다.

'스프링' 카테고리의 다른 글

h2 console  (0) 2021.11.27
form과 button  (0) 2021.11.21
스프링 프로젝트 기본 설정하기  (0) 2021.11.12
이클립스 패키지 프리젠테이션  (0) 2021.11.10
Spring Legacy Project 를 처음 만들며 느낀점  (1) 2021.11.07

스프링 프로젝트에 필요한 기본 설정은 한글을 읽게 하기 위해 인코딩 방식을 UTF-8 방식을 쓰게끔 만드는 것이다. 

상당 메뉴에 Window -> Preferences를 선택한다.

상단메뉴의 Window -> Preferences
General -> Workspace -> Text file encoding(UTF-8)
원하는 프로젝트의 인코딩 방식이 UTF-8이 되었는지 체크한다.
자바 버전을 1.8로 설정한다.

src -> main -> weaapp -> WEB-INF -> web.xml 파일을 열고 </servlet-mapping> 태그 아래에 아래의 코드를 추가한다.

<!--  문자 인코딩  시작 -->
<filter>
  <filter-name>encodingFilter</filter-name>
  <filter-class>
    org.springframework.web.filter.CharacterEncodingFilter
  </filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<!--  문자 인코딩  끝 -->

src -> main -> webapp -> WEB-INF -> views -> home.jsp 파일을 열고 <html>태그 위에 아래 코드를 추가한다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

pom.xml에서 자바 버전 1.6을 1.8로 바꾼다.

 

'스프링' 카테고리의 다른 글

h2 console  (0) 2021.11.27
form과 button  (0) 2021.11.21
스프링 프로젝트 base Url 설정  (1) 2021.11.12
이클립스 패키지 프리젠테이션  (0) 2021.11.10
Spring Legacy Project 를 처음 만들며 느낀점  (1) 2021.11.07

+ Recent posts