[C#] FluentFTP 샘플 프로그램 작성 (클린 아키텍처 적용)


목적

  • FluentFTP 를 이용한 간단한 FTP 프로그램을 Clean Architecture 측면으로 샘플 프로젝트 작성 진행합니다.
  • FTP 라이브러리는 추후에 바뀔 수 있으나, 샘플 프로젝트에서는 FluentFTP NuGet Package 를 이용하였습니다.

프로젝트 구조

  • 프로젝트 구조는 다음과 같습니다.

  • 위와 같이 프로젝트 구조를 나눴습니다.

Application 폴더

  • Application 폴더 안에 있는 클래스들 코드는 다음과 같습니다.

IFTPManager.cs

  • IFTPManager 인터페이스에는 크게 DownLoad, Upload, Delete, GetFiles 메서드를 정의하였습니다.
using FtpManager.Domain;

namespace FtpManager.Application
{
    public interface IFTPManager
    {
        public bool DownLoad(string sourcePath, string targetPath);
        public bool UpLoad(string sourcePath, string targetPath);
        public bool Delete(string targetPath);
        public FtpFiles GetFiles(string directory);
    }
}

Common 폴더

  • Commmon 폴더에는 프로젝트 공통으로 쓰이는 클래스들을 모아둡니다.
  • 여기서는 ValueObject.cs 클래스가 있습니다.

ValueObject.cs

  • ValueObject.cs 클래스는 값객체 역할을 나타내는 클래스 입니다.
namespace FtpManager.Common
{
    public abstract class ValueObject
    {
        protected static bool EqualOperator(ValueObject left, ValueObject right)
        {
            if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
            {
                return false;
            }
            return ReferenceEquals(left, null) || left.Equals(right);
        }

        protected static bool NotEqualOperator(ValueObject left, ValueObject right)
        {
            return !(EqualOperator(left, right));
        }

        protected abstract IEnumerable<object> GetEqualityComponents();

        public override bool Equals(object obj)
        {
            if (obj == null || obj.GetType() != GetType())
            {
                return false;
            }

            var other = (ValueObject)obj;

            return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
        }

        public override int GetHashCode()
        {
            return GetEqualityComponents()
                .Select(x => x != null ? x.GetHashCode() : 0)
                .Aggregate((x, y) => x ^ y);
        }

        public ValueObject GetCopy()
        {
            return this.MemberwiseClone() as ValueObject;
        }
    }
}

Domain 폴더

  • 도메인 역할을 하는 클래스들이 있습니다.
  • 여기서는 FtpConnectionInfo.cs, FtpFiles.cs, FtpProtocolEnum.cs 3개의 도메인 클래스가 있습니다.

FtpConnectionInfo.cs

using FtpManager.FtpConnectionInformation;

namespace FtpManager.Domain
{
    public class FtpConnectionInfo
    {
        public FtpIp FtpIp { get; private set; }
        public FtpPassword FtpPassword { get; private set; }
        public FtpUser FtpUser { get; private set; }
        public FtpHomeAddress HomeAddress { get; private set; }
        public FtpProtocol FtpProtocol { get; private set; }

        public FtpConnectionInfo(
            FtpIp ftpIp,
            FtpPassword ftpPassword,
            FtpUser ftpUser,
            FtpHomeAddress ftpHomeAddress,
            FtpProtocol ftpProtocol)
        {
            FtpIp = ftpIp;
            FtpPassword = ftpPassword;
            FtpUser = ftpUser;
            HomeAddress = ftpHomeAddress;
            FtpProtocol = ftpProtocol;
        }
    }
}

FtpFiles.cs

using FtpManager.FtpConnectionValueObject;

namespace FtpManager.Domain
{
    public class FtpFiles
    {
        private List<FtpFile> _files = new();

        public void AddFile(FtpFile file)
        {
            _files.Add(file);
        }

        public List<FtpFile> GetFiles()
        {
            return _files;
        }
    }
}

FtpProtocolEnum.cs

namespace FtpManager.Domain
{
    public enum FtpProtocol
    {
        FTP = 0,
        SFTP = 1
    }
}

FtpConnectionValueObject 폴더

  • 다음은 ValueObject 역할을 하는 클래스들이 모여있는 FtpConnectionValueObject 폴더 입니다.
  • 여기에는 Ftp 접속에 필요한 IP, Password, User 등의 값객체 역할을 하는 클래스들이 있습니다.

FtpFile.cs

using FtpManager.Common;

namespace FtpManager.FtpConnectionValueObject
{
    public class FtpFile : ValueObject
    {
        public string File { get; set; }

        public FtpFile(string file)
        {
            File = file;
        }

        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return File;
        }
    }
}

FtpHomeAddress.cs

using FtpManager.Common;

namespace FtpManager.FtpConnectionInformation
{
    public class FtpHomeAddress : ValueObject
    {
        public string HomeAddress { get; set; }

        public FtpHomeAddress(string homeAddress)
        {
            HomeAddress = homeAddress;
        }

        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return HomeAddress;
        }
    }
}

FtpIp.cs

using FtpManager.Common;

namespace FtpManager.FtpConnectionInformation
{
    public class FtpIp : ValueObject
    {
        public string Ip { get; set; }

        public FtpIp(string ip)
        {
            Ip = ip;
        }

        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return Ip;
        }
    }
}

FtpPassword.cs

using FtpManager.Common;

namespace FtpManager.FtpConnectionInformation
{
    public class FtpPassword : ValueObject
    {
        public string Password { get; set; }

        public FtpPassword(string password)
        {
            Password = password;
        }

        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return Password;
        }
    }
}

FtpUser.cs

using FtpManager.Common;

namespace FtpManager.FtpConnectionInformation
{
    public class FtpUser : ValueObject
    {
        public string User { get; set; }

        public FtpUser(string user)
        {
            User = user;
        }

        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return User;
        }
    }
}

Service 폴더

  • Service 폴더 안에는 실제 IFTPManager 인터페이스를 상속받아 각종 FTP 메서드를 구현하는 구현체 클래스가 있습니다.

FluentFtpTransfer.cs

  • 현재 샘플 프로젝트에서는 FluentFtp NuGet Package 를 설치하여 FluentFtp 로 FTP 관련 메서드들을 작성하였습니다.
using FluentFTP;
using FtpManager.Application;
using FtpManager.Domain;
using FtpManager.FtpConnectionValueObject;

namespace FtpManager
{
    public class FluentFtpTransfer : IFTPManager
    {
        readonly FtpClient ftpClient = null!;

        public FluentFtpTransfer(FtpConnectionInfo connectionInfo)
        {
            using var ftpClient = new FtpClient(connectionInfo.FtpIp.Ip, connectionInfo.FtpUser.User, connectionInfo.FtpPassword.Password);
            ftpClient.AutoConnect();
        }

        public bool DownLoad(string sourcePath, string targetPath)
        {
            if(ftpClient.DownloadFile(sourcePath, targetPath) == FtpStatus.Success)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public FtpFiles GetFiles(string directory)
        {
            FtpFiles files = new();

            FtpListItem[] fileList = ftpClient.GetListing(directory, FtpListOption.AllFiles)
                                              .Where(x => x.Type == FtpFileSystemObjectType.File)
                                              .OrderBy(x => x.Modified)
                                              .ToArray();

            foreach (FtpListItem item in fileList)
            {
                FtpFile file = new(item.Name);
                files.AddFile(file);
            }

            return files;
        }

        public bool Delete(string targetPath)
        {
            ftpClient.DeleteFile(targetPath);

            return true;
        }

        public bool UpLoad(string sourcePath, string targetPath)
        {
            Console.WriteLine($"{nameof(UpLoad)}");

            if(ftpClient.UploadFile(sourcePath, targetPath) == FtpStatus.Success)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

728x90

이 글을 공유하기

댓글

Designed by JB FACTORY